shithub: purgatorio

ref: 75c92428225428c8fde2d015f010e608a0b12f1d
dir: /utils/libmach/8db.c/

View raw version
#include <lib9.h>
#include <bio.h>
#include "mach.h"

/*
 * i386-specific debugger interface
 * also amd64 extensions
 */

static	char	*i386excep(Map*, Rgetter);

static	int	i386trace(Map*, uvlong, uvlong, uvlong, Tracer);
static	uvlong	i386frame(Map*, uvlong, uvlong, uvlong, uvlong);
static	int	i386foll(Map*, uvlong, Rgetter, uvlong*);
static	int	i386inst(Map*, uvlong, char, char*, int);
static	int	i386das(Map*, uvlong, char*, int);
static	int	i386instlen(Map*, uvlong);

static	char	STARTSYM[] =	"_main";
static	char	PROFSYM[] =	"_mainp";
static	char	FRAMENAME[] =	".frame";
static char *excname[65] =
{
/*[0]*/	"divide error",
/*[1]*/	"debug exception",
/*[2]*/	nil,
/*[3]*/	nil,
/*[4]*/	"overflow",
/*[5]*/	"bounds check",
/*[6]*/	"invalid opcode",
/*[7]*/	"math coprocessor emulation",
/*[8]*/	"double fault",
/*[9]*/	"math coprocessor overrun",
/*[10]*/	"invalid TSS",
/*[11]*/	"segment not present",
/*[12]*/	"stack exception",
/*[13]*/	"general protection violation",
/*[14]*/	"page fault",
/*[15]*/	nil,
/*[16]*/	"math coprocessor error",
/*[17]*/	"alignment check",
/*[18]*/	"machine check",
/*[19]*/	"floating-point exception",
/*[20]*/	nil,
/*[21]*/	nil,
/*[22]*/	nil,
/*[23]*/	nil,
/*[24]*/	"clock",
/*[25]*/	"keyboard",
/*[26]*/	nil,
/*[27]*/	"modem status",
/*[28]*/	"serial line status",
/*[29]*/	nil,
/*[30]*/	"floppy disk",
/*[31]*/	nil,
/*[32]*/	nil,
/*[33]*/	nil,
/*[34]*/	nil,
/*[35]*/	nil,
/*[36]*/	"mouse",
/*[37]*/	"math coprocessor",
/*[38]*/	"hard disk",
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,/* 39-54 */
	0,0,0,0,0,0,0,0,0,		/* 55-63 */
/*[64]*/	"system call",
};

Machdata i386mach =
{
	{0xCC, 0, 0, 0},	/* break point: INT 3 */
	1,			/* break point size */

	leswab,			/* convert short to local byte order */
	leswal,			/* convert long to local byte order */
	leswav,			/* convert vlong to local byte order */
	i386trace,		/* C traceback */
	i386frame,		/* frame finder */
	i386excep,		/* print exception */
	0,			/* breakpoint fixup */
	leieeesftos,		/* single precision float printer */
	leieeedftos,		/* double precision float printer */
	i386foll,		/* following addresses */
	i386inst,		/* print instruction */
	i386das,		/* dissembler */
	i386instlen,		/* instruction size calculation */
};

static char*
i386excep(Map *map, Rgetter rget)
{
	ulong c;
	uvlong pc;
	static char buf[16];

	c = (*rget)(map, "TRAP");
	if(c > 64 || excname[c] == 0) {
		if (c == 3) {
			pc = (*rget)(map, "PC");
			if (get1(map, pc, (uchar*)buf, machdata->bpsize) > 0)
			if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0)
				return "breakpoint";
		}
		snprint(buf, sizeof(buf), "exception %ld", c);
		return buf;
	} else
		return excname[c];
}

static int
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
	int i;
	uvlong osp;
	Symbol s, f;

	USED(link);
	i = 0;
	osp = 0;
	while(findsym(pc, CTEXT, &s)) {
		if (osp == sp)
			break;
		osp = sp;

		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
			break;

		if(pc != s.value) {	/* not at first instruction */
			if(findlocal(&s, FRAMENAME, &f) == 0)
				break;
			sp += f.value-mach->szaddr;
		}

		if (geta(map, sp, &pc) < 0)
			break;

		if(pc == 0)
			break;

		(*trace)(map, pc, sp, &s);
		sp += mach->szaddr;

		if(++i > 1000)
			break;
	}
	return i;
}

static uvlong
i386frame(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
{
	Symbol s, f;

	USED(link);
	while (findsym(pc, CTEXT, &s)) {
		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
			break;

		if(pc != s.value) {	/* not first instruction */
			if(findlocal(&s, FRAMENAME, &f) == 0)
				break;
			sp += f.value-mach->szaddr;
		}

		if (s.value == addr)
			return sp;

		if (geta(map, sp, &pc) < 0)
			break;
		sp += mach->szaddr;
	}
	return 0;
}

	/* I386/486 - Disassembler and related functions */

/*
 *  an instruction
 */
typedef struct Instr Instr;
struct	Instr
{
	uchar	mem[1+1+1+1+2+1+1+4+4];		/* raw instruction */
	uvlong	addr;		/* address of start of instruction */
	int	n;		/* number of bytes in instruction */
	char	*prefix;	/* instr prefix */
	char	*segment;	/* segment override */
	uchar	jumptype;	/* set to the operand type for jump/ret/call */
	uchar	amd64;
	uchar	rex;		/* REX prefix (or zero) */
	char	osize;		/* 'W' or 'L' (or 'Q' on amd64) */
	char	asize;		/* address size 'W' or 'L' (or 'Q' or amd64) */
	uchar	mod;		/* bits 6-7 of mod r/m field */
	uchar	reg;		/* bits 3-5 of mod r/m field */
	char	ss;		/* bits 6-7 of SIB */
	char	index;		/* bits 3-5 of SIB */
	char	base;		/* bits 0-2 of SIB */
	char	rip;		/* RIP-relative in amd64 mode */
	uchar	opre;		/* f2/f3 could introduce media */
	short	seg;		/* segment of far address */
	ulong	disp;		/* displacement */
	ulong 	imm;		/* immediate */
	ulong 	imm2;		/* second immediate operand */
	uvlong	imm64;		/* big immediate */
	char	*curr;		/* fill level in output buffer */
	char	*end;		/* end of output buffer */
	char	*err;		/* error message */
};

	/* 386 register (ha!) set */
enum{
	AX=0,
	CX,
	DX,
	BX,
	SP,
	BP,
	SI,
	DI,

	/* amd64 */
	R8,
	R9,
	R10,
	R11,
	R12,
	R13,
	R14,
	R15
};

	/* amd64 rex extension byte */
enum{
	REXW		= 1<<3,	/* =1, 64-bit operand size */
	REXR		= 1<<2,	/* extend modrm reg */
	REXX		= 1<<1,	/* extend sib index */
	REXB		= 1<<0	/* extend modrm r/m, sib base, or opcode reg */
};
	
	/* Operand Format codes */
/*
%A	-	address size register modifier (!asize -> 'E')
%C	-	Control register CR0/CR1/CR2
%D	-	Debug register DR0/DR1/DR2/DR3/DR6/DR7
%I	-	second immediate operand
%O	-	Operand size register modifier (!osize -> 'E')
%T	-	Test register TR6/TR7
%S	-	size code ('W' or 'L')
%W	-	Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
%d	-	displacement 16-32 bits
%e	-	effective address - Mod R/M value
%f	-	floating point register F0-F7 - from Mod R/M register
%g	-	segment register
%i	-	immediate operand 8-32 bits
%p	-	PC-relative - signed displacement in immediate field
%r	-	Reg from Mod R/M
%w	-	Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
*/

typedef struct Optable Optable;
struct Optable
{
	int	x;
	char	operand[2];
	void	*proto;		/* actually either (char*) or (Optable*) */
};
	/* Operand decoding codes */
enum {
	Ib = 1,			/* 8-bit immediate - (no sign extension)*/
	Ibs,			/* 8-bit immediate (sign extended) */
	Jbs,			/* 8-bit sign-extended immediate in jump or call */
	Iw,			/* 16-bit immediate -> imm */
	Iw2,			/* 16-bit immediate -> imm2 */
	Iwd,			/* Operand-sized immediate (no sign extension)*/
	Iwdq,			/* Operand-sized immediate, possibly 64 bits */
	Awd,			/* Address offset */
	Iwds,			/* Operand-sized immediate (sign extended) */
	RM,			/* Word or long R/M field with register (/r) */
	RMB,			/* Byte R/M field with register (/r) */
	RMOP,			/* Word or long R/M field with op code (/digit) */
	RMOPB,			/* Byte R/M field with op code (/digit) */
	RMR,			/* R/M register only (mod = 11) */
	RMM,			/* R/M memory only (mod = 0/1/2) */
	R0,			/* Base reg of Mod R/M is literal 0x00 */
	R1,			/* Base reg of Mod R/M is literal 0x01 */
	FRMOP,			/* Floating point R/M field with opcode */
	FRMEX,			/* Extended floating point R/M field with opcode */
	JUMP,			/* Jump or Call flag - no operand */
	RET,			/* Return flag - no operand */
	OA,			/* literal 0x0a byte */
	PTR,			/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
	AUX,			/* Multi-byte op code - Auxiliary table */
	AUXMM,			/* multi-byte op code - auxiliary table chosen by prefix */
	PRE,			/* Instr Prefix */
	OPRE,			/* Instr Prefix or media op extension */
	SEG,			/* Segment Prefix */
	OPOVER,			/* Operand size override */
	ADDOVER,		/* Address size override */
};
	
static Optable optab0F00[8]=
{
0x00,	0,0,		"MOVW	LDT,%e",
0x01,	0,0,		"MOVW	TR,%e",
0x02,	0,0,		"MOVW	%e,LDT",
0x03,	0,0,		"MOVW	%e,TR",
0x04,	0,0,		"VERR	%e",
0x05,	0,0,		"VERW	%e",
};

static Optable optab0F01[8]=
{
0x00,	0,0,		"MOVL	GDTR,%e",
0x01,	0,0,		"MOVL	IDTR,%e",
0x02,	0,0,		"MOVL	%e,GDTR",
0x03,	0,0,		"MOVL	%e,IDTR",
0x04,	0,0,		"MOVW	MSW,%e",	/* word */
0x06,	0,0,		"MOVW	%e,MSW",	/* word */
0x07,	0,0,		"INVLPG	%e",		/* or SWAPGS */
};

static Optable optab0F01F8[1]=
{
0x00,	0,0,		"SWAPGS",
};

/* 0F71 */
/* 0F72 */
/* 0F73 */

static Optable optab0FAE[8]=
{
0x00,	0,0,		"FXSAVE	%e",
0x01,	0,0,		"FXRSTOR	%e",
0x02,	0,0,		"LDMXCSR	%e",
0x03,	0,0,		"STMXCSR	%e",
0x05,	0,0,		"LFENCE",
0x06,	0,0,		"MFENCE",
0x07,	0,0,		"SFENCE",
};

/* 0F18 */
/* 0F0D */

static Optable optab0FBA[8]=
{
0x04,	Ib,0,		"BT%S	%i,%e",
0x05,	Ib,0,		"BTS%S	%i,%e",
0x06,	Ib,0,		"BTR%S	%i,%e",
0x07,	Ib,0,		"BTC%S	%i,%e",
};

static Optable optab0F0F[256]=
{
0x0c,	0,0,		"PI2FW	%m,%M",
0x0d,	0,0,		"PI2L	%m,%M",
0x1c,	0,0,		"PF2IW	%m,%M",
0x1d,	0,0,		"PF2IL	%m,%M",
0x8a,	0,0,		"PFNACC	%m,%M",
0x8e,	0,0,		"PFPNACC	%m,%M",
0x90,	0,0,		"PFCMPGE	%m,%M",
0x94,	0,0,		"PFMIN	%m,%M",
0x96,	0,0,		"PFRCP	%m,%M",
0x97,	0,0,		"PFRSQRT	%m,%M",
0x9a,	0,0,		"PFSUB	%m,%M",
0x9e,	0,0,		"PFADD	%m,%M",
0xa0,	0,0,		"PFCMPGT	%m,%M",
0xa4,	0,0,		"PFMAX	%m,%M",
0xa6,	0,0,		"PFRCPIT1	%m,%M",
0xa7,	0,0,		"PFRSQIT1	%m,%M",
0xaa,	0,0,		"PFSUBR	%m,%M",
0xae,	0,0,		"PFACC	%m,%M",
0xb0,	0,0,		"PFCMPEQ	%m,%M",
0xb4,	0,0,		"PFMUL	%m,%M",
0xb6,	0,0,		"PFRCPI2T	%m,%M",
0xb7,	0,0,		"PMULHRW	%m,%M",
0xbb,	0,0,		"PSWAPL	%m,%M",
};

static Optable optab0FC7[8]=
{
0x01,	0,0,		"CMPXCHG8B	%e",
};

static Optable optab660F71[8]=
{
0x02,	Ib,0,		"PSRLW	%i,%X",
0x04,	Ib,0,		"PSRAW	%i,%X",
0x06,	Ib,0,		"PSLLW	%i,%X",
};

static Optable optab660F72[8]=
{
0x02,	Ib,0,		"PSRLL	%i,%X",
0x04,	Ib,0,		"PSRAL	%i,%X",
0x06,	Ib,0,		"PSLLL	%i,%X",
};

static Optable optab660F73[8]=
{
0x02,	Ib,0,		"PSRLQ	%i,%X",
0x03,	Ib,0,		"PSRLO	%i,%X",
0x06,	Ib,0,		"PSLLQ	%i,%X",
0x07,	Ib,0,		"PSLLO	%i,%X",
};

static Optable optab660F[256]=
{
0x2B,	RM,0,		"MOVNTPD	%x,%e",
0x2E,	RM,0,		"UCOMISD	%x,%X",
0x2F,	RM,0,		"COMISD	%x,%X",
0x5A,	RM,0,		"CVTPD2PS	%x,%X",
0x5B,	RM,0,		"CVTPS2PL	%x,%X",
0x6A,	RM,0,		"PUNPCKHLQ %x,%X",
0x6B,	RM,0,		"PACKSSLW %x,%X",
0x6C,	RM,0,		"PUNPCKLQDQ %x,%X",
0x6D,	RM,0,		"PUNPCKHQDQ %x,%X",
0x6E,	RM,0,		"MOV%S	%e,%X",
0x6F,	RM,0,		"MOVO	%x,%X",		/* MOVDQA */
0x70,	RM,Ib,		"PSHUFL	%i,%x,%X",
0x71,	RMOP,0,		optab660F71,
0x72,	RMOP,0,		optab660F72,
0x73,	RMOP,0,		optab660F73,
0x7E,	RM,0,		"MOV%S	%X,%e",
0x7F,	RM,0,		"MOVO	%X,%x",
0xC4,	RM,Ib,		"PINSRW	%i,%e,%X",
0xC5,	RMR,Ib,		"PEXTRW	%i,%X,%e",
0xD4,	RM,0,		"PADDQ	%x,%X",
0xD5,	RM,0,		"PMULLW	%x,%X",
0xD6,	RM,0,		"MOVQ	%X,%x",
0xE6,	RM,0,		"CVTTPD2PL	%x,%X",
0xE7,	RM,0,		"MOVNTO	%X,%e",
0xF7,	RM,0,		"MASKMOVOU	%x,%X",
};

static Optable optabF20F[256]=
{
0x10,	RM,0,		"MOVSD	%x,%X",
0x11,	RM,0,		"MOVSD	%X,%x",
0x2A,	RM,0,		"CVTS%S2SD	%e,%X",
0x2C,	RM,0,		"CVTTSD2S%S	%x,%r",
0x2D,	RM,0,		"CVTSD2S%S	%x,%r",
0x5A,	RM,0,		"CVTSD2SS	%x,%X",
0x6F,	RM,0,		"MOVOU	%x,%X",
0x70,	RM,Ib,		"PSHUFLW	%i,%x,%X",
0x7F,	RM,0,		"MOVOU	%X,%x",
0xD6,	RM,0,		"MOVQOZX	%M,%X",
0xE6,	RM,0,		"CVTPD2PL	%x,%X",
};

static Optable optabF30F[256]=
{
0x10,	RM,0,		"MOVSS	%x,%X",
0x11,	RM,0,		"MOVSS	%X,%x",
0x2A,	RM,0,		"CVTS%S2SS	%e,%X",
0x2C,	RM,0,		"CVTTSS2S%S	%x,%r",
0x2D,	RM,0,		"CVTSS2S%S	%x,%r",
0x5A,	RM,0,		"CVTSS2SD	%x,%X",
0x5B,	RM,0,		"CVTTPS2PL	%x,%X",
0x6F,	RM,0,		"MOVOU	%x,%X",
0x70,	RM,Ib,		"PSHUFHW	%i,%x,%X",
0x7E,	RM,0,		"MOVQOZX	%x,%X",
0x7F,	RM,0,		"MOVOU	%X,%x",
0xD6,	RM,0,		"MOVQOZX	%m*,%X",
0xE6,	RM,0,		"CVTPL2PD	%x,%X",
};

static Optable optab0F[256]=
{
0x00,	RMOP,0,		optab0F00,
0x01,	RMOP,0,		optab0F01,
0x02,	RM,0,		"LAR	%e,%r",
0x03,	RM,0,		"LSL	%e,%r",
0x05,	0,0,		"SYSCALL",
0x06,	0,0,		"CLTS",
0x07,	0,0,		"SYSRET",
0x08,	0,0,		"INVD",
0x09,	0,0,		"WBINVD",
0x0B,	0,0,		"UD2",
0x0F,	RM,AUX,		optab0F0F,		/* 3DNow! */
0x10,	RM,0,		"MOVU%s	%x,%X",
0x11,	RM,0,		"MOVU%s	%X,%x",
0x12,	RM,0,		"MOV[H]L%s	%x,%X",	/* TO DO: H if source is XMM */
0x13,	RM,0,		"MOVL%s	%X,%e",
0x14,	RM,0,		"UNPCKL%s	%x,%X",
0x15,	RM,0,		"UNPCKH%s	%x,%X",
0x16,	RM,0,		"MOV[L]H%s	%x,%X",	/* TO DO: L if source is XMM */
0x17,	RM,0,		"MOVH%s	%X,%x",
0x20,	RMR,0,		"MOVL	%C,%e",
0x21,	RMR,0,		"MOVL	%D,%e",
0x22,	RMR,0,		"MOVL	%e,%C",
0x23,	RMR,0,		"MOVL	%e,%D",
0x24,	RMR,0,		"MOVL	%T,%e",
0x26,	RMR,0,		"MOVL	%e,%T",
0x28,	RM,0,		"MOVA%s	%x,%X",
0x29,	RM,0,		"MOVA%s	%X,%x",
0x2A,	RM,0,		"CVTPL2%s	%m*,%X",
0x2B,	RM,0,		"MOVNT%s	%X,%e",
0x2C,	RM,0,		"CVTT%s2PL	%x,%M",
0x2D,	RM,0,		"CVT%s2PL	%x,%M",
0x2E,	RM,0,		"UCOMISS	%x,%X",
0x2F,	RM,0,		"COMISS	%x,%X",
0x30,	0,0,		"WRMSR",
0x31,	0,0,		"RDTSC",
0x32,	0,0,		"RDMSR",
0x33,	0,0,		"RDPMC",
0x42,	RM,0,		"CMOVC	%e,%r",		/* CF */
0x43,	RM,0,		"CMOVNC	%e,%r",		/* ¬ CF */
0x44,	RM,0,		"CMOVZ	%e,%r",		/* ZF */
0x45,	RM,0,		"CMOVNZ	%e,%r",		/* ¬ ZF */
0x46,	RM,0,		"CMOVBE	%e,%r",		/* CF ∨ ZF */
0x47,	RM,0,		"CMOVA	%e,%r",		/* ¬CF ∧ ¬ZF */
0x48,	RM,0,		"CMOVS	%e,%r",		/* SF */
0x49,	RM,0,		"CMOVNS	%e,%r",		/* ¬ SF */
0x4A,	RM,0,		"CMOVP	%e,%r",		/* PF */
0x4B,	RM,0,		"CMOVNP	%e,%r",		/* ¬ PF */
0x4C,	RM,0,		"CMOVLT	%e,%r",		/* LT ≡ OF ≠ SF */
0x4D,	RM,0,		"CMOVGE	%e,%r",		/* GE ≡ ZF ∨ SF */
0x4E,	RM,0,		"CMOVLE	%e,%r",		/* LE ≡ ZF ∨ LT */
0x4F,	RM,0,		"CMOVGT	%e,%r",		/* GT ≡ ¬ZF ∧ GE */
0x50,	RM,0,		"MOVMSK%s	%X,%r",	/* TO DO: check */
0x51,	RM,0,		"SQRT%s	%x,%X",
0x52,	RM,0,		"RSQRT%s	%x,%X",
0x53,	RM,0,		"RCP%s	%x,%X",
0x54,	RM,0,		"AND%s	%x,%X",
0x55,	RM,0,		"ANDN%s	%x,%X",
0x56,	RM,0,		"OR%s	%x,%X",		/* TO DO: S/D */
0x57,	RM,0,		"XOR%s	%x,%X",		/* S/D */
0x58,	RM,0,		"ADD%s	%x,%X",		/* S/P S/D */
0x59,	RM,0,		"MUL%s	%x,%X",
0x5A,	RM,0,		"CVTPS2PD	%x,%X",
0x5B,	RM,0,		"CVTPL2PS	%x,%X",
0x5C,	RM,0,		"SUB%s	%x,%X",
0x5D,	RM,0,		"MIN%s	%x,%X",
0x5E,	RM,0,		"DIV%s	%x,%X",		/* TO DO: S/P S/D */
0x5F,	RM,0,		"MAX%s	%x,%X",
0x60,	RM,0,		"PUNPCKLBW %m,%M",
0x61,	RM,0,		"PUNPCKLWL %m,%M",
0x62,	RM,0,		"PUNPCKLLQ %m,%M",
0x63,	RM,0,		"PACKSSWB %m,%M",
0x64,	RM,0,		"PCMPGTB %m,%M",
0x65,	RM,0,		"PCMPGTW %m,%M",
0x66,	RM,0,		"PCMPGTL %m,%M",
0x67,	RM,0,		"PACKUSWB %m,%M",
0x68,	RM,0,		"PUNPCKHBW %m,%M",
0x69,	RM,0,		"PUNPCKHWL %m,%M",
0x6A,	RM,0,		"PUNPCKHLQ %m,%M",
0x6B,	RM,0,		"PACKSSLW %m,%M",
0x6E,	RM,0,		"MOV%S %e,%M",
0x6F,	RM,0,		"MOVQ %m,%M",
0x70,	RM,Ib,		"PSHUFW	%i,%m,%M",
0x74,	RM,0,		"PCMPEQB %m,%M",
0x75,	RM,0,		"PCMPEQW %m,%M",
0x76,	RM,0,		"PCMPEQL %m,%M",
0x7E,	RM,0,		"MOV%S %M,%e",
0x7F,	RM,0,		"MOVQ %M,%m",
0xAE,	RMOP,0,		optab0FAE,
0xAA,	0,0,		"RSM",
0xB0,	RM,0,		"CMPXCHGB	%r,%e",
0xB1,	RM,0,		"CMPXCHG%S	%r,%e",
0xC0,	RMB,0,		"XADDB	%r,%e",
0xC1,	RM,0,		"XADD%S	%r,%e",
0xC2,	RM,Ib,		"CMP%s	%i,%x,%X",
0xC3,	RM,0,		"MOVNTI%S	%r,%e",
0xC6,	RM,Ib,		"SHUF%s	%i,%x,%X",
0xC8,	0,0,		"BSWAP	AX",
0xC9,	0,0,		"BSWAP	CX",
0xCA,	0,0,		"BSWAP	DX",
0xCB,	0,0,		"BSWAP	BX",
0xCC,	0,0,		"BSWAP	SP",
0xCD,	0,0,		"BSWAP	BP",
0xCE,	0,0,		"BSWAP	SI",
0xCF,	0,0,		"BSWAP	DI",
0xD1,	RM,0,		"PSRLW %m,%M",
0xD2,	RM,0,		"PSRLL %m,%M",
0xD3,	RM,0,		"PSRLQ %m,%M",
0xD5,	RM,0,		"PMULLW %m,%M",
0xD6,	RM,0,		"MOVQOZX	%m*,%X",
0xD7,	RM,0,		"PMOVMSKB %m,%r",
0xD8,	RM,0,		"PSUBUSB %m,%M",
0xD9,	RM,0,		"PSUBUSW %m,%M",
0xDA,	RM,0,		"PMINUB %m,%M",
0xDB,	RM,0,		"PAND %m,%M",
0xDC,	RM,0,		"PADDUSB %m,%M",
0xDD,	RM,0,		"PADDUSW %m,%M",
0xDE,	RM,0,		"PMAXUB %m,%M",
0xDF,	RM,0,		"PANDN %m,%M",
0xE0,	RM,0,		"PAVGB %m,%M",
0xE1,	RM,0,		"PSRAW %m,%M",
0xE2,	RM,0,		"PSRAL %m,%M",
0xE3,	RM,0,		"PAVGW %m,%M",
0xE4,	RM,0,		"PMULHUW %m,%M",
0xE5,	RM,0,		"PMULHW %m,%M",
0xE7,	RM,0,		"MOVNTQ	%M,%e",
0xE8,	RM,0,		"PSUBSB %m,%M",
0xE9,	RM,0,		"PSUBSW %m,%M",
0xEA,	RM,0,		"PMINSW %m,%M",
0xEB,	RM,0,		"POR %m,%M",
0xEC,	RM,0,		"PADDSB %m,%M",
0xED,	RM,0,		"PADDSW %m,%M",
0xEE,	RM,0,		"PMAXSW %m,%M",
0xEF,	RM,0,		"PXOR %m,%M",
0xF1,	RM,0,		"PSLLW %m,%M",
0xF2,	RM,0,		"PSLLL %m,%M",
0xF3,	RM,0,		"PSLLQ %m,%M",
0xF4,	RM,0,		"PMULULQ	%m,%M",
0xF5,	RM,0,		"PMADDWL %m,%M",
0xF6,	RM,0,		"PSADBW %m,%M",
0xF7,	RMR,0,		"MASKMOVQ	%m,%M",
0xF8,	RM,0,		"PSUBB %m,%M",
0xF9,	RM,0,		"PSUBW %m,%M",
0xFA,	RM,0,		"PSUBL %m,%M",
0xFC,	RM,0,		"PADDB %m,%M",
0xFD,	RM,0,		"PADDW %m,%M",
0xFE,	RM,0,		"PADDL %m,%M",

0x80,	Iwds,0,		"JOS	%p",
0x81,	Iwds,0,		"JOC	%p",
0x82,	Iwds,0,		"JCS	%p",
0x83,	Iwds,0,		"JCC	%p",
0x84,	Iwds,0,		"JEQ	%p",
0x85,	Iwds,0,		"JNE	%p",
0x86,	Iwds,0,		"JLS	%p",
0x87,	Iwds,0,		"JHI	%p",
0x88,	Iwds,0,		"JMI	%p",
0x89,	Iwds,0,		"JPL	%p",
0x8a,	Iwds,0,		"JPS	%p",
0x8b,	Iwds,0,		"JPC	%p",
0x8c,	Iwds,0,		"JLT	%p",
0x8d,	Iwds,0,		"JGE	%p",
0x8e,	Iwds,0,		"JLE	%p",
0x8f,	Iwds,0,		"JGT	%p",
0x90,	RMB,0,		"SETOS	%e",
0x91,	RMB,0,		"SETOC	%e",
0x92,	RMB,0,		"SETCS	%e",
0x93,	RMB,0,		"SETCC	%e",
0x94,	RMB,0,		"SETEQ	%e",
0x95,	RMB,0,		"SETNE	%e",
0x96,	RMB,0,		"SETLS	%e",
0x97,	RMB,0,		"SETHI	%e",
0x98,	RMB,0,		"SETMI	%e",
0x99,	RMB,0,		"SETPL	%e",
0x9a,	RMB,0,		"SETPS	%e",
0x9b,	RMB,0,		"SETPC	%e",
0x9c,	RMB,0,		"SETLT	%e",
0x9d,	RMB,0,		"SETGE	%e",
0x9e,	RMB,0,		"SETLE	%e",
0x9f,	RMB,0,		"SETGT	%e",
0xa0,	0,0,		"PUSHL	FS",
0xa1,	0,0,		"POPL	FS",
0xa2,	0,0,		"CPUID",
0xa3,	RM,0,		"BT%S	%r,%e",
0xa4,	RM,Ib,		"SHLD%S	%r,%i,%e",
0xa5,	RM,0,		"SHLD%S	%r,CL,%e",
0xa8,	0,0,		"PUSHL	GS",
0xa9,	0,0,		"POPL	GS",
0xab,	RM,0,		"BTS%S	%r,%e",
0xac,	RM,Ib,		"SHRD%S	%r,%i,%e",
0xad,	RM,0,		"SHRD%S	%r,CL,%e",
0xaf,		RM,0,		"IMUL%S	%e,%r",
0xb2,	RMM,0,		"LSS	%e,%r",
0xb3,	RM,0,		"BTR%S	%r,%e",
0xb4,	RMM,0,		"LFS	%e,%r",
0xb5,	RMM,0,		"LGS	%e,%r",
0xb6,	RMB,0,		"MOVBZX	%e,%R",
0xb7,	RM,0,		"MOVWZX	%e,%R",
0xba,	RMOP,0,		optab0FBA,
0xbb,	RM,0,		"BTC%S	%e,%r",
0xbc,	RM,0,		"BSF%S	%e,%r",
0xbd,	RM,0,		"BSR%S	%e,%r",
0xbe,	RMB,0,		"MOVBSX	%e,%R",
0xbf,	RM,0,		"MOVWSX	%e,%R",
0xc7,	RMOP,0,		optab0FC7,
};

static Optable optab80[8]=
{
0x00,	Ib,0,		"ADDB	%i,%e",
0x01,	Ib,0,		"ORB	%i,%e",
0x02,	Ib,0,		"ADCB	%i,%e",
0x03,	Ib,0,		"SBBB	%i,%e",
0x04,	Ib,0,		"ANDB	%i,%e",
0x05,	Ib,0,		"SUBB	%i,%e",
0x06,	Ib,0,		"XORB	%i,%e",
0x07,	Ib,0,		"CMPB	%e,%i",
};

static Optable optab81[8]=
{
0x00,	Iwd,0,		"ADD%S	%i,%e",
0x01,	Iwd,0,		"OR%S	%i,%e",
0x02,	Iwd,0,		"ADC%S	%i,%e",
0x03,	Iwd,0,		"SBB%S	%i,%e",
0x04,	Iwd,0,		"AND%S	%i,%e",
0x05,	Iwd,0,		"SUB%S	%i,%e",
0x06,	Iwd,0,		"XOR%S	%i,%e",
0x07,	Iwd,0,		"CMP%S	%e,%i",
};

static Optable optab83[8]=
{
0x00,	Ibs,0,		"ADD%S	%i,%e",
0x01,	Ibs,0,		"OR%S	%i,%e",
0x02,	Ibs,0,		"ADC%S	%i,%e",
0x03,	Ibs,0,		"SBB%S	%i,%e",
0x04,	Ibs,0,		"AND%S	%i,%e",
0x05,	Ibs,0,		"SUB%S	%i,%e",
0x06,	Ibs,0,		"XOR%S	%i,%e",
0x07,	Ibs,0,		"CMP%S	%e,%i",
};

static Optable optabC0[8] =
{
0x00,	Ib,0,		"ROLB	%i,%e",
0x01,	Ib,0,		"RORB	%i,%e",
0x02,	Ib,0,		"RCLB	%i,%e",
0x03,	Ib,0,		"RCRB	%i,%e",
0x04,	Ib,0,		"SHLB	%i,%e",
0x05,	Ib,0,		"SHRB	%i,%e",
0x07,	Ib,0,		"SARB	%i,%e",
};

static Optable optabC1[8] =
{
0x00,	Ib,0,		"ROL%S	%i,%e",
0x01,	Ib,0,		"ROR%S	%i,%e",
0x02,	Ib,0,		"RCL%S	%i,%e",
0x03,	Ib,0,		"RCR%S	%i,%e",
0x04,	Ib,0,		"SHL%S	%i,%e",
0x05,	Ib,0,		"SHR%S	%i,%e",
0x07,	Ib,0,		"SAR%S	%i,%e",
};

static Optable optabD0[8] =
{
0x00,	0,0,		"ROLB	%e",
0x01,	0,0,		"RORB	%e",
0x02,	0,0,		"RCLB	%e",
0x03,	0,0,		"RCRB	%e",
0x04,	0,0,		"SHLB	%e",
0x05,	0,0,		"SHRB	%e",
0x07,	0,0,		"SARB	%e",
};

static Optable optabD1[8] =
{
0x00,	0,0,		"ROL%S	%e",
0x01,	0,0,		"ROR%S	%e",
0x02,	0,0,		"RCL%S	%e",
0x03,	0,0,		"RCR%S	%e",
0x04,	0,0,		"SHL%S	%e",
0x05,	0,0,		"SHR%S	%e",
0x07,	0,0,		"SAR%S	%e",
};

static Optable optabD2[8] =
{
0x00,	0,0,		"ROLB	CL,%e",
0x01,	0,0,		"RORB	CL,%e",
0x02,	0,0,		"RCLB	CL,%e",
0x03,	0,0,		"RCRB	CL,%e",
0x04,	0,0,		"SHLB	CL,%e",
0x05,	0,0,		"SHRB	CL,%e",
0x07,	0,0,		"SARB	CL,%e",
};

static Optable optabD3[8] =
{
0x00,	0,0,		"ROL%S	CL,%e",
0x01,	0,0,		"ROR%S	CL,%e",
0x02,	0,0,		"RCL%S	CL,%e",
0x03,	0,0,		"RCR%S	CL,%e",
0x04,	0,0,		"SHL%S	CL,%e",
0x05,	0,0,		"SHR%S	CL,%e",
0x07,	0,0,		"SAR%S	CL,%e",
};

static Optable optabD8[8+8] =
{
0x00,	0,0,		"FADDF	%e,F0",
0x01,	0,0,		"FMULF	%e,F0",
0x02,	0,0,		"FCOMF	%e,F0",
0x03,	0,0,		"FCOMFP	%e,F0",
0x04,	0,0,		"FSUBF	%e,F0",
0x05,	0,0,		"FSUBRF	%e,F0",
0x06,	0,0,		"FDIVF	%e,F0",
0x07,	0,0,		"FDIVRF	%e,F0",
0x08,	0,0,		"FADDD	%f,F0",
0x09,	0,0,		"FMULD	%f,F0",
0x0a,	0,0,		"FCOMD	%f,F0",
0x0b,	0,0,		"FCOMPD	%f,F0",
0x0c,	0,0,		"FSUBD	%f,F0",
0x0d,	0,0,		"FSUBRD	%f,F0",
0x0e,	0,0,		"FDIVD	%f,F0",
0x0f,	0,0,		"FDIVRD	%f,F0",
};
/*
 *	optabD9 and optabDB use the following encoding: 
 *	if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
 *	else instruction = optabDx[(modrm&0x3f)+8];
 *
 *	the instructions for MOD == 3, follow the 8 instructions
 *	for the other MOD values stored at the front of the table.
 */
static Optable optabD9[64+8] =
{
0x00,	0,0,		"FMOVF	%e,F0",
0x02,	0,0,		"FMOVF	F0,%e",
0x03,	0,0,		"FMOVFP	F0,%e",
0x04,	0,0,		"FLDENV%S %e",
0x05,	0,0,		"FLDCW	%e",
0x06,	0,0,		"FSTENV%S %e",
0x07,	0,0,		"FSTCW	%e",
0x08,	0,0,		"FMOVD	F0,F0",		/* Mod R/M = 11xx xxxx*/
0x09,	0,0,		"FMOVD	F1,F0",
0x0a,	0,0,		"FMOVD	F2,F0",
0x0b,	0,0,		"FMOVD	F3,F0",
0x0c,	0,0,		"FMOVD	F4,F0",
0x0d,	0,0,		"FMOVD	F5,F0",
0x0e,	0,0,		"FMOVD	F6,F0",
0x0f,	0,0,		"FMOVD	F7,F0",
0x10,	0,0,		"FXCHD	F0,F0",
0x11,	0,0,		"FXCHD	F1,F0",
0x12,	0,0,		"FXCHD	F2,F0",
0x13,	0,0,		"FXCHD	F3,F0",
0x14,	0,0,		"FXCHD	F4,F0",
0x15,	0,0,		"FXCHD	F5,F0",
0x16,	0,0,		"FXCHD	F6,F0",
0x17,	0,0,		"FXCHD	F7,F0",
0x18,	0,0,		"FNOP",
0x28,	0,0,		"FCHS",
0x29,	0,0,		"FABS",
0x2c,	0,0,		"FTST",
0x2d,	0,0,		"FXAM",
0x30,	0,0,		"FLD1",
0x31,	0,0,		"FLDL2T",
0x32,	0,0,		"FLDL2E",
0x33,	0,0,		"FLDPI",
0x34,	0,0,		"FLDLG2",
0x35,	0,0,		"FLDLN2",
0x36,	0,0,		"FLDZ",
0x38,	0,0,		"F2XM1",
0x39,	0,0,		"FYL2X",
0x3a,	0,0,		"FPTAN",
0x3b,	0,0,		"FPATAN",
0x3c,	0,0,		"FXTRACT",
0x3d,	0,0,		"FPREM1",
0x3e,	0,0,		"FDECSTP",
0x3f,	0,0,		"FNCSTP",
0x40,	0,0,		"FPREM",
0x41,	0,0,		"FYL2XP1",
0x42,	0,0,		"FSQRT",
0x43,	0,0,		"FSINCOS",
0x44,	0,0,		"FRNDINT",
0x45,	0,0,		"FSCALE",
0x46,	0,0,		"FSIN",
0x47,	0,0,		"FCOS",
};

static Optable optabDA[8+8] =
{
0x00,	0,0,		"FADDL	%e,F0",
0x01,	0,0,		"FMULL	%e,F0",
0x02,	0,0,		"FCOML	%e,F0",
0x03,	0,0,		"FCOMLP	%e,F0",
0x04,	0,0,		"FSUBL	%e,F0",
0x05,	0,0,		"FSUBRL	%e,F0",
0x06,	0,0,		"FDIVL	%e,F0",
0x07,	0,0,		"FDIVRL	%e,F0",
0x0d,	R1,0,		"FUCOMPP",
};

static Optable optabDB[8+64] =
{
0x00,	0,0,		"FMOVL	%e,F0",
0x02,	0,0,		"FMOVL	F0,%e",
0x03,	0,0,		"FMOVLP	F0,%e",
0x05,	0,0,		"FMOVX	%e,F0",
0x07,	0,0,		"FMOVXP	F0,%e",
0x2a,	0,0,		"FCLEX",
0x2b,	0,0,		"FINIT",
};

static Optable optabDC[8+8] =
{
0x00,	0,0,		"FADDD	%e,F0",
0x01,	0,0,		"FMULD	%e,F0",
0x02,	0,0,		"FCOMD	%e,F0",
0x03,	0,0,		"FCOMDP	%e,F0",
0x04,	0,0,		"FSUBD	%e,F0",
0x05,	0,0,		"FSUBRD	%e,F0",
0x06,	0,0,		"FDIVD	%e,F0",
0x07,	0,0,		"FDIVRD	%e,F0",
0x08,	0,0,		"FADDD	F0,%f",
0x09,	0,0,		"FMULD	F0,%f",
0x0c,	0,0,		"FSUBRD	F0,%f",
0x0d,	0,0,		"FSUBD	F0,%f",
0x0e,	0,0,		"FDIVRD	F0,%f",
0x0f,	0,0,		"FDIVD	F0,%f",
};

static Optable optabDD[8+8] =
{
0x00,	0,0,		"FMOVD	%e,F0",
0x02,	0,0,		"FMOVD	F0,%e",
0x03,	0,0,		"FMOVDP	F0,%e",
0x04,	0,0,		"FRSTOR%S %e",
0x06,	0,0,		"FSAVE%S %e",
0x07,	0,0,		"FSTSW	%e",
0x08,	0,0,		"FFREED	%f",
0x0a,	0,0,		"FMOVD	%f,F0",
0x0b,	0,0,		"FMOVDP	%f,F0",
0x0c,	0,0,		"FUCOMD	%f,F0",
0x0d,	0,0,		"FUCOMDP %f,F0",
};

static Optable optabDE[8+8] =
{
0x00,	0,0,		"FADDW	%e,F0",
0x01,	0,0,		"FMULW	%e,F0",
0x02,	0,0,		"FCOMW	%e,F0",
0x03,	0,0,		"FCOMWP	%e,F0",
0x04,	0,0,		"FSUBW	%e,F0",
0x05,	0,0,		"FSUBRW	%e,F0",
0x06,	0,0,		"FDIVW	%e,F0",
0x07,	0,0,		"FDIVRW	%e,F0",
0x08,	0,0,		"FADDDP	F0,%f",
0x09,	0,0,		"FMULDP	F0,%f",
0x0b,	R1,0,		"FCOMPDP",
0x0c,	0,0,		"FSUBRDP F0,%f",
0x0d,	0,0,		"FSUBDP	F0,%f",
0x0e,	0,0,		"FDIVRDP F0,%f",
0x0f,	0,0,		"FDIVDP	F0,%f",
};

static Optable optabDF[8+8] =
{
0x00,	0,0,		"FMOVW	%e,F0",
0x02,	0,0,		"FMOVW	F0,%e",
0x03,	0,0,		"FMOVWP	F0,%e",
0x04,	0,0,		"FBLD	%e",
0x05,	0,0,		"FMOVL	%e,F0",
0x06,	0,0,		"FBSTP	%e",
0x07,	0,0,		"FMOVLP	F0,%e",
0x0c,	R0,0,		"FSTSW	%OAX",
};

static Optable optabF6[8] =
{
0x00,	Ib,0,		"TESTB	%i,%e",
0x02,	0,0,		"NOTB	%e",
0x03,	0,0,		"NEGB	%e",
0x04,	0,0,		"MULB	AL,%e",
0x05,	0,0,		"IMULB	AL,%e",
0x06,	0,0,		"DIVB	AL,%e",
0x07,	0,0,		"IDIVB	AL,%e",
};

static Optable optabF7[8] =
{
0x00,	Iwd,0,		"TEST%S	%i,%e",
0x02,	0,0,		"NOT%S	%e",
0x03,	0,0,		"NEG%S	%e",
0x04,	0,0,		"MUL%S	%OAX,%e",
0x05,	0,0,		"IMUL%S	%OAX,%e",
0x06,	0,0,		"DIV%S	%OAX,%e",
0x07,	0,0,		"IDIV%S	%OAX,%e",
};

static Optable optabFE[8] =
{
0x00,	0,0,		"INCB	%e",
0x01,	0,0,		"DECB	%e",
};

static Optable optabFF[8] =
{
0x00,	0,0,		"INC%S	%e",
0x01,	0,0,		"DEC%S	%e",
0x02,	JUMP,0,		"CALL*	%e",
0x03,	JUMP,0,		"CALLF*	%e",
0x04,	JUMP,0,		"JMP*	%e",
0x05,	JUMP,0,		"JMPF*	%e",
0x06,	0,0,		"PUSHL	%e",
};

static Optable optable[256+1] =
{
0x00,	RMB,0,		"ADDB	%r,%e",
0x01,	RM,0,		"ADD%S	%r,%e",
0x02,	RMB,0,		"ADDB	%e,%r",
0x03,	RM,0,		"ADD%S	%e,%r",
0x04,	Ib,0,		"ADDB	%i,AL",
0x05,	Iwd,0,		"ADD%S	%i,%OAX",
0x06,	0,0,		"PUSHL	ES",
0x07,	0,0,		"POPL	ES",
0x08,	RMB,0,		"ORB	%r,%e",
0x09,	RM,0,		"OR%S	%r,%e",
0x0a,	RMB,0,		"ORB	%e,%r",
0x0b,	RM,0,		"OR%S	%e,%r",
0x0c,	Ib,0,		"ORB	%i,AL",
0x0d,	Iwd,0,		"OR%S	%i,%OAX",
0x0e,	0,0,		"PUSHL	CS",
0x0f,	AUXMM,0,	optab0F,
0x10,	RMB,0,		"ADCB	%r,%e",
0x11,	RM,0,		"ADC%S	%r,%e",
0x12,	RMB,0,		"ADCB	%e,%r",
0x13,	RM,0,		"ADC%S	%e,%r",
0x14,	Ib,0,		"ADCB	%i,AL",
0x15,	Iwd,0,		"ADC%S	%i,%OAX",
0x16,	0,0,		"PUSHL	SS",
0x17,	0,0,		"POPL	SS",
0x18,	RMB,0,		"SBBB	%r,%e",
0x19,	RM,0,		"SBB%S	%r,%e",
0x1a,	RMB,0,		"SBBB	%e,%r",
0x1b,	RM,0,		"SBB%S	%e,%r",
0x1c,	Ib,0,		"SBBB	%i,AL",
0x1d,	Iwd,0,		"SBB%S	%i,%OAX",
0x1e,	0,0,		"PUSHL	DS",
0x1f,	0,0,		"POPL	DS",
0x20,	RMB,0,		"ANDB	%r,%e",
0x21,	RM,0,		"AND%S	%r,%e",
0x22,	RMB,0,		"ANDB	%e,%r",
0x23,	RM,0,		"AND%S	%e,%r",
0x24,	Ib,0,		"ANDB	%i,AL",
0x25,	Iwd,0,		"AND%S	%i,%OAX",
0x26,	SEG,0,		"ES:",
0x27,	0,0,		"DAA",
0x28,	RMB,0,		"SUBB	%r,%e",
0x29,	RM,0,		"SUB%S	%r,%e",
0x2a,	RMB,0,		"SUBB	%e,%r",
0x2b,	RM,0,		"SUB%S	%e,%r",
0x2c,	Ib,0,		"SUBB	%i,AL",
0x2d,	Iwd,0,		"SUB%S	%i,%OAX",
0x2e,	SEG,0,		"CS:",
0x2f,	0,0,		"DAS",
0x30,	RMB,0,		"XORB	%r,%e",
0x31,	RM,0,		"XOR%S	%r,%e",
0x32,	RMB,0,		"XORB	%e,%r",
0x33,	RM,0,		"XOR%S	%e,%r",
0x34,	Ib,0,		"XORB	%i,AL",
0x35,	Iwd,0,		"XOR%S	%i,%OAX",
0x36,	SEG,0,		"SS:",
0x37,	0,0,		"AAA",
0x38,	RMB,0,		"CMPB	%r,%e",
0x39,	RM,0,		"CMP%S	%r,%e",
0x3a,	RMB,0,		"CMPB	%e,%r",
0x3b,	RM,0,		"CMP%S	%e,%r",
0x3c,	Ib,0,		"CMPB	%i,AL",
0x3d,	Iwd,0,		"CMP%S	%i,%OAX",
0x3e,	SEG,0,		"DS:",
0x3f,	0,0,		"AAS",
0x40,	0,0,		"INC%S	%OAX",
0x41,	0,0,		"INC%S	%OCX",
0x42,	0,0,		"INC%S	%ODX",
0x43,	0,0,		"INC%S	%OBX",
0x44,	0,0,		"INC%S	%OSP",
0x45,	0,0,		"INC%S	%OBP",
0x46,	0,0,		"INC%S	%OSI",
0x47,	0,0,		"INC%S	%ODI",
0x48,	0,0,		"DEC%S	%OAX",
0x49,	0,0,		"DEC%S	%OCX",
0x4a,	0,0,		"DEC%S	%ODX",
0x4b,	0,0,		"DEC%S	%OBX",
0x4c,	0,0,		"DEC%S	%OSP",
0x4d,	0,0,		"DEC%S	%OBP",
0x4e,	0,0,		"DEC%S	%OSI",
0x4f,	0,0,		"DEC%S	%ODI",
0x50,	0,0,		"PUSH%S	%OAX",
0x51,	0,0,		"PUSH%S	%OCX",
0x52,	0,0,		"PUSH%S	%ODX",
0x53,	0,0,		"PUSH%S	%OBX",
0x54,	0,0,		"PUSH%S	%OSP",
0x55,	0,0,		"PUSH%S	%OBP",
0x56,	0,0,		"PUSH%S	%OSI",
0x57,	0,0,		"PUSH%S	%ODI",
0x58,	0,0,		"POP%S	%OAX",
0x59,	0,0,		"POP%S	%OCX",
0x5a,	0,0,		"POP%S	%ODX",
0x5b,	0,0,		"POP%S	%OBX",
0x5c,	0,0,		"POP%S	%OSP",
0x5d,	0,0,		"POP%S	%OBP",
0x5e,	0,0,		"POP%S	%OSI",
0x5f,	0,0,		"POP%S	%ODI",
0x60,	0,0,		"PUSHA%S",
0x61,	0,0,		"POPA%S",
0x62,	RMM,0,		"BOUND	%e,%r",
0x63,	RM,0,		"ARPL	%r,%e",
0x64,	SEG,0,		"FS:",
0x65,	SEG,0,		"GS:",
0x66,	OPOVER,0,	"",
0x67,	ADDOVER,0,	"",
0x68,	Iwd,0,		"PUSH%S	%i",
0x69,	RM,Iwd,		"IMUL%S	%e,%i,%r",
0x6a,	Ib,0,		"PUSH%S	%i",
0x6b,	RM,Ibs,		"IMUL%S	%e,%i,%r",
0x6c,	0,0,		"INSB	DX,(%ODI)",
0x6d,	0,0,		"INS%S	DX,(%ODI)",
0x6e,	0,0,		"OUTSB	(%ASI),DX",
0x6f,	0,0,		"OUTS%S	(%ASI),DX",
0x70,	Jbs,0,		"JOS	%p",
0x71,	Jbs,0,		"JOC	%p",
0x72,	Jbs,0,		"JCS	%p",
0x73,	Jbs,0,		"JCC	%p",
0x74,	Jbs,0,		"JEQ	%p",
0x75,	Jbs,0,		"JNE	%p",
0x76,	Jbs,0,		"JLS	%p",
0x77,	Jbs,0,		"JHI	%p",
0x78,	Jbs,0,		"JMI	%p",
0x79,	Jbs,0,		"JPL	%p",
0x7a,	Jbs,0,		"JPS	%p",
0x7b,	Jbs,0,		"JPC	%p",
0x7c,	Jbs,0,		"JLT	%p",
0x7d,	Jbs,0,		"JGE	%p",
0x7e,	Jbs,0,		"JLE	%p",
0x7f,	Jbs,0,		"JGT	%p",
0x80,	RMOPB,0,	optab80,
0x81,	RMOP,0,		optab81,
0x83,	RMOP,0,		optab83,
0x84,	RMB,0,		"TESTB	%r,%e",
0x85,	RM,0,		"TEST%S	%r,%e",
0x86,	RMB,0,		"XCHGB	%r,%e",
0x87,	RM,0,		"XCHG%S	%r,%e",
0x88,	RMB,0,		"MOVB	%r,%e",
0x89,	RM,0,		"MOV%S	%r,%e",
0x8a,	RMB,0,		"MOVB	%e,%r",
0x8b,	RM,0,		"MOV%S	%e,%r",
0x8c,	RM,0,		"MOVW	%g,%e",
0x8d,	RM,0,		"LEA%S	%e,%r",
0x8e,	RM,0,		"MOVW	%e,%g",
0x8f,	RM,0,		"POP%S	%e",
0x90,	0,0,		"NOP",
0x91,	0,0,		"XCHG	%OCX,%OAX",
0x92,	0,0,		"XCHG	%ODX,%OAX",
0x93,	0,0,		"XCHG	%OBX,%OAX",
0x94,	0,0,		"XCHG	%OSP,%OAX",
0x95,	0,0,		"XCHG	%OBP,%OAX",
0x96,	0,0,		"XCHG	%OSI,%OAX",
0x97,	0,0,		"XCHG	%ODI,%OAX",
0x98,	0,0,		"%W",			/* miserable CBW or CWDE */
0x99,	0,0,		"%w",			/* idiotic CWD or CDQ */
0x9a,	PTR,0,		"CALL%S	%d",
0x9b,	0,0,		"WAIT",
0x9c,	0,0,		"PUSHF",
0x9d,	0,0,		"POPF",
0x9e,	0,0,		"SAHF",
0x9f,	0,0,		"LAHF",
0xa0,	Awd,0,		"MOVB	%i,AL",
0xa1,	Awd,0,		"MOV%S	%i,%OAX",
0xa2,	Awd,0,		"MOVB	AL,%i",
0xa3,	Awd,0,		"MOV%S	%OAX,%i",
0xa4,	0,0,		"MOVSB	(%ASI),(%ADI)",
0xa5,	0,0,		"MOVS%S	(%ASI),(%ADI)",
0xa6,	0,0,		"CMPSB	(%ASI),(%ADI)",
0xa7,	0,0,		"CMPS%S	(%ASI),(%ADI)",
0xa8,	Ib,0,		"TESTB	%i,AL",
0xa9,	Iwd,0,		"TEST%S	%i,%OAX",
0xaa,	0,0,		"STOSB	AL,(%ADI)",
0xab,	0,0,		"STOS%S	%OAX,(%ADI)",
0xac,	0,0,		"LODSB	(%ASI),AL",
0xad,	0,0,		"LODS%S	(%ASI),%OAX",
0xae,	0,0,		"SCASB	(%ADI),AL",
0xaf,	0,0,		"SCAS%S	(%ADI),%OAX",
0xb0,	Ib,0,		"MOVB	%i,AL",
0xb1,	Ib,0,		"MOVB	%i,CL",
0xb2,	Ib,0,		"MOVB	%i,DL",
0xb3,	Ib,0,		"MOVB	%i,BL",
0xb4,	Ib,0,		"MOVB	%i,AH",
0xb5,	Ib,0,		"MOVB	%i,CH",
0xb6,	Ib,0,		"MOVB	%i,DH",
0xb7,	Ib,0,		"MOVB	%i,BH",
0xb8,	Iwdq,0,		"MOV%S	%i,%OAX",
0xb9,	Iwdq,0,		"MOV%S	%i,%OCX",
0xba,	Iwdq,0,		"MOV%S	%i,%ODX",
0xbb,	Iwdq,0,		"MOV%S	%i,%OBX",
0xbc,	Iwdq,0,		"MOV%S	%i,%OSP",
0xbd,	Iwdq,0,		"MOV%S	%i,%OBP",
0xbe,	Iwdq,0,		"MOV%S	%i,%OSI",
0xbf,	Iwdq,0,		"MOV%S	%i,%ODI",
0xc0,	RMOPB,0,	optabC0,
0xc1,	RMOP,0,		optabC1,
0xc2,	Iw,0,		"RET	%i",
0xc3,	RET,0,		"RET",
0xc4,	RM,0,		"LES	%e,%r",
0xc5,	RM,0,		"LDS	%e,%r",
0xc6,	RMB,Ib,		"MOVB	%i,%e",
0xc7,	RM,Iwd,		"MOV%S	%i,%e",
0xc8,	Iw2,Ib,		"ENTER	%i,%I",		/* loony ENTER */
0xc9,	RET,0,		"LEAVE",		/* bizarre LEAVE */
0xca,	Iw,0,		"RETF	%i",
0xcb,	RET,0,		"RETF",
0xcc,	0,0,		"INT	3",
0xcd,	Ib,0,		"INTB	%i",
0xce,	0,0,		"INTO",
0xcf,	0,0,		"IRET",
0xd0,	RMOPB,0,	optabD0,
0xd1,	RMOP,0,		optabD1,
0xd2,	RMOPB,0,	optabD2,
0xd3,	RMOP,0,		optabD3,
0xd4,	OA,0,		"AAM",
0xd5,	OA,0,		"AAD",
0xd7,	0,0,		"XLAT",
0xd8,	FRMOP,0,	optabD8,
0xd9,	FRMEX,0,	optabD9,
0xda,	FRMOP,0,	optabDA,
0xdb,	FRMEX,0,	optabDB,
0xdc,	FRMOP,0,	optabDC,
0xdd,	FRMOP,0,	optabDD,
0xde,	FRMOP,0,	optabDE,
0xdf,	FRMOP,0,	optabDF,
0xe0,	Jbs,0,		"LOOPNE	%p",
0xe1,	Jbs,0,		"LOOPE	%p",
0xe2,	Jbs,0,		"LOOP	%p",
0xe3,	Jbs,0,		"JCXZ	%p",
0xe4,	Ib,0,		"INB	%i,AL",
0xe5,	Ib,0,		"IN%S	%i,%OAX",
0xe6,	Ib,0,		"OUTB	AL,%i",
0xe7,	Ib,0,		"OUT%S	%OAX,%i",
0xe8,	Iwds,0,		"CALL	%p",
0xe9,	Iwds,0,		"JMP	%p",
0xea,	PTR,0,		"JMP	%d",
0xeb,	Jbs,0,		"JMP	%p",
0xec,	0,0,		"INB	DX,AL",
0xed,	0,0,		"IN%S	DX,%OAX",
0xee,	0,0,		"OUTB	AL,DX",
0xef,	0,0,		"OUT%S	%OAX,DX",
0xf0,	PRE,0,		"LOCK",
0xf2,	OPRE,0,		"REPNE",
0xf3,	OPRE,0,		"REP",
0xf4,	0,0,		"HLT",
0xf5,	0,0,		"CMC",
0xf6,	RMOPB,0,	optabF6,
0xf7,	RMOP,0,		optabF7,
0xf8,	0,0,		"CLC",
0xf9,	0,0,		"STC",
0xfa,	0,0,		"CLI",
0xfb,	0,0,		"STI",
0xfc,	0,0,		"CLD",
0xfd,	0,0,		"STD",
0xfe,	RMOPB,0,	optabFE,
0xff,	RMOP,0,		optabFF,
0x100,	RM,0,		"MOVLQSX	%r,%e",
};

static struct {
	Optable	*tab;
	uint	nel;
} optabtab[] = {
	optab0F00, nelem(optab0F00),
	optab0F01, nelem(optab0F01),
	optab0F01F8, nelem(optab0F01F8),
	optab0FAE, nelem(optab0FAE),
	optab0FBA, nelem(optab0FBA),
	optab0F0F, nelem(optab0F0F),
	optab0FC7, nelem(optab0FC7),
	optab660F71, nelem(optab660F71),
	optab660F72, nelem(optab660F72),
	optab660F73, nelem(optab660F73),
	optab660F, nelem(optab660F),
	optabF20F, nelem(optabF20F),
	optabF30F, nelem(optabF30F),
	optab0F, nelem(optab0F),
	optab80, nelem(optab80),
	optab81, nelem(optab81),
	optab83, nelem(optab83),
	optabC0, nelem(optabC0),
	optabC1, nelem(optabC1),
	optabD0, nelem(optabD0),
	optabD1, nelem(optabD1),
	optabD2, nelem(optabD2),
	optabD3, nelem(optabD3),
	optabD8, nelem(optabD8),
	optabD9, nelem(optabD9),
	optabDA, nelem(optabDA),
	optabDB, nelem(optabDB),
	optabDC, nelem(optabDC),
	optabDD, nelem(optabDD),
	optabDE, nelem(optabDE),
	optabDF, nelem(optabDF),
	optabF6, nelem(optabF6),
	optabF7, nelem(optabF7),
	optabFE, nelem(optabFE),
	optabFF, nelem(optabFF),
	optable, nelem(optable),
};

/*
 * compensate for Microsoft's ageing compilers
 */
static void
ordertab(Optable *tab, int nel)
{
	int i, x;
	static Optable empty;

	for(i = nel; --i >= 0;){
		x = tab[i].x;
		if(x != i){
			tab[x] = tab[i];
			tab[i] = empty;
		}
	}
}

static void
soptoms(void)
{
	int i;
	static int reordered;	/* assumes non-concurrent use */

	if(reordered)
		return;
	reordered = 1;
	for(i = 0; i < nelem(optabtab); i++)
		ordertab(optabtab[i].tab, optabtab[i].nel);
}

/*
 *  get a byte of the instruction
 */
static int
igetc(Map *map, Instr *ip, uchar *c)
{
	if(ip->n+1 > sizeof(ip->mem)){
		werrstr("instruction too long");
		return -1;
	}
	if (get1(map, ip->addr+ip->n, c, 1) < 0) {
		werrstr("can't read instruction: %r");
		return -1;
	}
	ip->mem[ip->n++] = *c;
	return 1;
}

/*
 *  get two bytes of the instruction
 */
static int
igets(Map *map, Instr *ip, ushort *sp)
{
	uchar c;
	ushort s;

	if (igetc(map, ip, &c) < 0)
		return -1;
	s = c;
	if (igetc(map, ip, &c) < 0)
		return -1;
	s |= (c<<8);
	*sp = s;
	return 1;
}

/*
 *  get 4 bytes of the instruction
 */
static int
igetl(Map *map, Instr *ip, ulong *lp)
{
	ushort s;
	long	l;

	if (igets(map, ip, &s) < 0)
		return -1;
	l = s;
	if (igets(map, ip, &s) < 0)
		return -1;
	l |= (s<<16);
	*lp = l;
	return 1;
}

/*
 *  get 8 bytes of the instruction
 */
static int
igetq(Map *map, Instr *ip, vlong *qp)
{
	ulong	l;
	uvlong q;

	if (igetl(map, ip, &l) < 0)
		return -1;
	q = l;
	if (igetl(map, ip, &l) < 0)
		return -1;
	q |= ((uvlong)l<<32);
	*qp = q;
	return 1;
}

static int
getdisp(Map *map, Instr *ip, int mod, int rm, int code, int pcrel)
{
	uchar c;
	ushort s;

	if (mod > 2)
		return 1;
	if (mod == 1) {
		if (igetc(map, ip, &c) < 0)
			return -1;
		if (c&0x80)
			ip->disp = c|0xffffff00;
		else
			ip->disp = c&0xff;
	} else if (mod == 2 || rm == code) {
		if (ip->asize == 'E') {
			if (igetl(map, ip, &ip->disp) < 0)
				return -1;
			if (mod == 0)
				ip->rip = pcrel;
		} else {
			if (igets(map, ip, &s) < 0)
				return -1;
			if (s&0x8000)
				ip->disp = s|0xffff0000;
			else
				ip->disp = s;
		}
		if (mod == 0)
			ip->base = -1;
	}
	return 1;
}

static int
modrm(Map *map, Instr *ip, uchar c)
{
	uchar rm, mod;

	mod = (c>>6)&3;
	rm = c&7;
	ip->mod = mod;
	ip->base = rm;
	ip->reg = (c>>3)&7;
	ip->rip = 0;
	if (mod == 3)			/* register */
		return 1;
	if (ip->asize == 0) {		/* 16-bit mode */
		switch(rm) {
		case 0:
			ip->base = BX; ip->index = SI;
			break;
		case 1:
			ip->base = BX; ip->index = DI;
			break;
		case 2:
			ip->base = BP; ip->index = SI;
			break;
		case 3:
			ip->base = BP; ip->index = DI;
			break;
		case 4:
			ip->base = SI;
			break;
		case 5:
			ip->base = DI;
			break;
		case 6:
			ip->base = BP;
			break;
		case 7:
			ip->base = BX;
			break;
		default:
			break;
		}
		return getdisp(map, ip, mod, rm, 6, 0);
	}
	if (rm == 4) {	/* scummy sib byte */
		if (igetc(map, ip, &c) < 0)
			return -1;
		ip->ss = (c>>6)&0x03;
		ip->index = (c>>3)&0x07;
		if (ip->index == 4)
			ip->index = -1;
		ip->base = c&0x07;
		return getdisp(map, ip, mod, ip->base, 5, 0);
	}
	return getdisp(map, ip, mod, rm, 5, ip->amd64);
}

static Optable *
mkinstr(Map *map, Instr *ip, uvlong pc)
{
	int i, n, norex;
	uchar c;
	ushort s;
	Optable *op, *obase;
	char buf[128];

	soptoms();
	memset(ip, 0, sizeof(*ip));
	norex = 1;
	ip->base = -1;
	ip->index = -1;
	if(asstype == AI8086)
		ip->osize = 'W';
	else {
		ip->osize = 'L';
		ip->asize = 'E';
		ip->amd64 = asstype != AI386;
		norex = 0;
	}
	ip->addr = pc;
	if (igetc(map, ip, &c) < 0)
		return 0;
	obase = optable;
newop:
	if(ip->amd64 && !norex){
		if(c >= 0x40 && c <= 0x4f) {
			ip->rex = c;
			if(igetc(map, ip, &c) < 0)
				return 0;
		}
		if(c == 0x63){
			op = &obase[0x100];	/* MOVLQSX */
			goto hack;
		}
	}
	op = &obase[c];
hack:
	if (op->proto == 0) {
badop:
		n = snprint(buf, sizeof(buf), "opcode: ??");
		for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
			_hexify(buf+n, ip->mem[i], 1);
		strcpy(buf+n, "??");
		werrstr(buf);
		return 0;
	}
	for(i = 0; i < 2 && op->operand[i]; i++) {
		switch(op->operand[i]) {
		case Ib:	/* 8-bit immediate - (no sign extension)*/
			if (igetc(map, ip, &c) < 0)
				return 0;
			ip->imm = c&0xff;
			ip->imm64 = ip->imm;
			break;
		case Jbs:	/* 8-bit jump immediate (sign extended) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (c&0x80)
				ip->imm = c|0xffffff00;
			else
				ip->imm = c&0xff;
			ip->imm64 = (long)ip->imm;
			ip->jumptype = Jbs;
			break;
		case Ibs:	/* 8-bit immediate (sign extended) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (c&0x80)
				if (ip->osize == 'L')
					ip->imm = c|0xffffff00;
				else
					ip->imm = c|0xff00;
			else
				ip->imm = c&0xff;
			ip->imm64 = (long)ip->imm;
			break;
		case Iw:	/* 16-bit immediate -> imm */
			if (igets(map, ip, &s) < 0)
				return 0;
			ip->imm = s&0xffff;
			ip->imm64 = ip->imm;
			ip->jumptype = Iw;
			break;
		case Iw2:	/* 16-bit immediate -> in imm2*/
			if (igets(map, ip, &s) < 0)
				return 0;
			ip->imm2 = s&0xffff;
			break;
		case Iwd:	/* Operand-sized immediate (no sign extension unless 64 bits)*/
			if (ip->osize == 'L') {
				if (igetl(map, ip, &ip->imm) < 0)
					return 0;
				ip->imm64 = ip->imm;
				if(ip->rex&REXW && (ip->imm & (1<<31)) != 0)
					ip->imm64 |= (vlong)~0 << 32;
			} else {
				if (igets(map, ip, &s)< 0)
					return 0;
				ip->imm = s&0xffff;
				ip->imm64 = ip->imm;
			}
			break;
		case Iwdq:	/* Operand-sized immediate, possibly big */
			if (ip->osize == 'L') {
				if (igetl(map, ip, &ip->imm) < 0)
					return 0;
				ip->imm64 = ip->imm;
				if (ip->rex & REXW) {
					ulong l;
					if (igetl(map, ip, &l) < 0)
						return 0;
					ip->imm64 |= (uvlong)l << 32;
				}
			} else {
				if (igets(map, ip, &s)< 0)
					return 0;
				ip->imm = s&0xffff;
			}
			break;
		case Awd:	/* Address-sized immediate (no sign extension)*/
			if (ip->asize == 'E') {
				if (igetl(map, ip, &ip->imm) < 0)
					return 0;
				/* TO DO: REX */
			} else {
				if (igets(map, ip, &s)< 0)
					return 0;
				ip->imm = s&0xffff;
			}
			break;
		case Iwds:	/* Operand-sized immediate (sign extended) */
			if (ip->osize == 'L') {
				if (igetl(map, ip, &ip->imm) < 0)
					return 0;
			} else {
				if (igets(map, ip, &s)< 0)
					return 0;
				if (s&0x8000)
					ip->imm = s|0xffff0000;
				else
					ip->imm = s&0xffff;
			}
			ip->jumptype = Iwds;
			break;
		case OA:	/* literal 0x0a byte */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (c != 0x0a)
				goto badop;
			break;
		case R0:	/* base register must be R0 */
			if (ip->base != 0)
				goto badop;
			break;
		case R1:	/* base register must be R1 */
			if (ip->base != 1)
				goto badop;
			break;
		case RMB:	/* R/M field with byte register (/r)*/
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			ip->osize = 'B';
			break;
		case RM:	/* R/M field with register (/r) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			break;
		case RMOPB:	/* R/M field with op code (/digit) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			c = ip->reg;		/* secondary op code */
			obase = (Optable*)op->proto;
			ip->osize = 'B';
			goto newop;
		case RMOP:	/* R/M field with op code (/digit) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			obase = (Optable*)op->proto;
			if(ip->amd64 && obase == optab0F01 && c == 0xF8)
				return optab0F01F8;
			c = ip->reg;
			goto newop;
		case FRMOP:	/* FP R/M field with op code (/digit) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			if ((c&0xc0) == 0xc0)
				c = ip->reg+8;		/* 16 entry table */
			else
				c = ip->reg;
			obase = (Optable*)op->proto;
			goto newop;
		case FRMEX:	/* Extended FP R/M field with op code (/digit) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (modrm(map, ip, c) < 0)
				return 0;
			if ((c&0xc0) == 0xc0)
				c = (c&0x3f)+8;		/* 64-entry table */
			else
				c = ip->reg;
			obase = (Optable*)op->proto;
			goto newop;
		case RMR:	/* R/M register only (mod = 11) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if ((c&0xc0) != 0xc0) {
				werrstr("invalid R/M register: %x", c);
				return 0;
			}
			if (modrm(map, ip, c) < 0)
				return 0;
			break;
		case RMM:	/* R/M register only (mod = 11) */
			if (igetc(map, ip, &c) < 0)
				return 0;
			if ((c&0xc0) == 0xc0) {
				werrstr("invalid R/M memory mode: %x", c);
				return 0;
			}
			if (modrm(map, ip, c) < 0)
				return 0;
			break;
		case PTR:	/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
			if (ip->osize == 'L') {
				if (igetl(map, ip, &ip->disp) < 0)
					return 0;
			} else {
				if (igets(map, ip, &s)< 0)
					return 0;
				ip->disp = s&0xffff;
			}
			if (igets(map, ip, (ushort*)&ip->seg) < 0)
				return 0;
			ip->jumptype = PTR;
			break;
		case AUXMM:	/* Multi-byte op code; prefix determines table selection */
			if (igetc(map, ip, &c) < 0)
				return 0;
			obase = (Optable*)op->proto;
			switch (ip->opre) {
			case 0x66:	op = optab660F; break;
			case 0xF2:	op = optabF20F; break;
			case 0xF3:	op = optabF30F; break;
			default:	op = nil; break;
			}
			if(op != nil && op[c].proto != nil)
				obase = op;
			norex = 1;	/* no more rex prefixes */
			/* otherwise the optab entry captures it */
			goto newop;
		case AUX:	/* Multi-byte op code - Auxiliary table */
			obase = (Optable*)op->proto;
			if (igetc(map, ip, &c) < 0)
				return 0;
			goto newop;
		case OPRE:	/* Instr Prefix or media op */
			ip->opre = c;
			/* fall through */
		case PRE:	/* Instr Prefix */
			ip->prefix = (char*)op->proto;
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (ip->opre && c == 0x0F)
				ip->prefix = 0;
			goto newop;
		case SEG:	/* Segment Prefix */
			ip->segment = (char*)op->proto;
			if (igetc(map, ip, &c) < 0)
				return 0;
			goto newop;
		case OPOVER:	/* Operand size override */
			ip->opre = c;
			ip->osize = 'W';
			if (igetc(map, ip, &c) < 0)
				return 0;
			if (c == 0x0F)
				ip->osize = 'L';
			else if (ip->amd64 && (c&0xF0) == 0x40)
				ip->osize = 'Q';
			goto newop;
		case ADDOVER:	/* Address size override */
			ip->asize = 0;
			if (igetc(map, ip, &c) < 0)
				return 0;
			goto newop;
		case JUMP:	/* mark instruction as JUMP or RET */
		case RET:
			ip->jumptype = op->operand[i];
			break;
		default:
			werrstr("bad operand type %d", op->operand[i]);
			return 0;
		}
	}
	return op;
}

#pragma	varargck	argpos	bprint		2

static void
bprint(Instr *ip, char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
	va_end(arg);
}

/*
 *  if we want to call 16 bit regs AX,BX,CX,...
 *  and 32 bit regs EAX,EBX,ECX,... then
 *  change the defs of ANAME and ONAME to:
 *  #define	ANAME(ip)	((ip->asize == 'E' ? "E" : "")
 *  #define	ONAME(ip)	((ip)->osize == 'L' ? "E" : "")
 */
#define	ANAME(ip)	""
#define	ONAME(ip)	""

static char *reg[] =  {
	"AX",
	"CX",
	"DX",
	"BX",
	"SP",
	"BP",
	"SI",
	"DI",

	/* amd64 */
	"R8",
	"R9",
	"R10",
	"R11",
	"R12",
	"R13",
	"R14",
	"R15",
};

static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
static char *breg64[] = { "AL", "CL", "DL", "BL", "SPB", "BPB", "SIB", "DIB",
	"R8B", "R9B", "R10B", "R11B", "R12B", "R13B", "R14B", "R15B" };
static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };

static void
plocal(Instr *ip)
{
	int ret;
	long offset;
	Symbol s;
	char *reg;

	offset = ip->disp;
	if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
		bprint(ip, "%lux(SP)", offset);
		return;
	}

	if (s.value > ip->disp) {
		ret = getauto(&s, s.value-ip->disp-mach->szaddr, CAUTO, &s);
		reg = "(SP)";
	} else {
		offset -= s.value;
		ret = getauto(&s, offset, CPARAM, &s);
		reg = "(FP)";
	}
	if (ret)
		bprint(ip, "%s+", s.name);
	else
		offset = ip->disp;
	bprint(ip, "%lux%s", offset, reg);
}

static int
isjmp(Instr *ip)
{
	switch(ip->jumptype){
	case Iwds:
	case Jbs:
	case JUMP:
		return 1;
	default:
		return 0;
	}
}

/*
 * This is too smart for its own good, but it really is nice
 * to have accurate translations when debugging, and it
 * helps us identify which code is different in binaries that
 * are changed on sources.
 */
static int
issymref(Instr *ip, Symbol *s, long w, long val)
{
	Symbol next, tmp;
	long isstring, size;

	if (isjmp(ip))
		return 1;
	if (s->class==CTEXT && w==0)
		return 1;
	if (s->class==CDATA) {
		/* use first bss symbol (or "end") rather than edata */
		if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
			if((s ->index >= 0 && globalsym(&tmp, s->index+1) && tmp.value==s->value)
			|| (s->index > 0 && globalsym(&tmp, s->index-1) && tmp.value==s->value))
				*s = tmp;
		}
		if (w == 0)
			return 1;
		for (next=*s; next.value==s->value; next=tmp)
			if (!globalsym(&tmp, next.index+1))
				break;
		size = next.value - s->value;
		if (w >= size)
			return 0;
		if (w > size-w)
			w = size-w;
		/* huge distances are usually wrong except in .string */
		isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
		if (w > 8192 && !isstring)
			return 0;
		/* medium distances are tricky - look for constants */
		/* near powers of two */
		if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
			return 0;
		return 1;
	}
	return 0;
}

static void
immediate(Instr *ip, vlong val)
{
	Symbol s;
	long w;

	if (findsym(val, CANY, &s)) {		/* TO DO */
		w = val - s.value;
		if (w < 0)
			w = -w;
		if (issymref(ip, &s, w, val)) {
			if (w)
				bprint(ip, "%s+%lux(SB)", s.name, w);
			else
				bprint(ip, "%s(SB)", s.name);
			return;
		}
/*
		if (s.class==CDATA && globalsym(&s, s.index+1)) {
			w = s.value - val;
			if (w < 0)
				w = -w;
			if (w < 4096) {
				bprint(ip, "%s-%lux(SB)", s.name, w);
				return;
			}
		}
*/
	}
	if((ip->rex & REXW) == 0)
		bprint(ip, "%lux", (long)val);
	else
		bprint(ip, "%llux", val);
}

static void
pea(Instr *ip)
{
	if (ip->mod == 3) {
		if (ip->osize == 'B')
			bprint(ip, (ip->rex & REXB? breg64: breg)[ip->base]);
		else if(ip->rex & REXB)
			bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
		else
			bprint(ip, "%s%s", ANAME(ip), reg[ip->base]);
		return;
	}
	if (ip->segment)
		bprint(ip, ip->segment);
	if (ip->asize == 'E' && ip->base == SP)
		plocal(ip);
	else {
		if (ip->base < 0)
			immediate(ip, ip->disp);
		else {
			bprint(ip, "%lux", ip->disp);
			if(ip->rip)
				bprint(ip, "(RIP)");
			bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]);
		}
	}
	if (ip->index >= 0)
		bprint(ip,"(%s%s*%d)", ANAME(ip), reg[ip->rex&REXX? ip->index+8: ip->index], 1<<ip->ss);
}

static void
prinstr(Instr *ip, char *fmt)
{
	vlong v;

	if (ip->prefix)
		bprint(ip, "%s ", ip->prefix);
	for (; *fmt && ip->curr < ip->end; fmt++) {
		if (*fmt != '%'){
			*ip->curr++ = *fmt;
			continue;
		}
		switch(*++fmt){
		case '%':
			*ip->curr++ = '%';
			break;
		case 'A':
			bprint(ip, "%s", ANAME(ip));
			break;
		case 'C':
			bprint(ip, "CR%d", ip->reg);
			break;
		case 'D':
			if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
				bprint(ip, "DR%d",ip->reg);
			else
				bprint(ip, "?");
			break;
		case 'I':
			bprint(ip, "$");
			immediate(ip, ip->imm2);
			break;
		case 'O':
			bprint(ip,"%s", ONAME(ip));
			break;
		case 'i':
			bprint(ip, "$");
			v = ip->imm;
			if(ip->rex & REXW)
				v = ip->imm64;
			immediate(ip, v);
			break;
		case 'R':
			bprint(ip, "%s%s", ONAME(ip), reg[ip->rex&REXR? ip->reg+8: ip->reg]);
			break;
		case 'S':
			if(ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
				bprint(ip, "Q");
			else
				bprint(ip, "%c", ip->osize);
			break;
		case 's':
			if(ip->opre == 0 || ip->opre == 0x66)
				bprint(ip, "P");
			else
				bprint(ip, "S");
			if(ip->opre == 0xf2 || ip->opre == 0x66)
				bprint(ip, "D");
			else
				bprint(ip, "S");
			break;
		case 'T':
			if (ip->reg == 6 || ip->reg == 7)
				bprint(ip, "TR%d",ip->reg);
			else
				bprint(ip, "?");
			break;
		case 'W':
			if (ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
				bprint(ip, "CDQE");
			else if (ip->osize == 'L')
				bprint(ip,"CWDE");
			else
				bprint(ip, "CBW");
			break;
		case 'd':
			bprint(ip,"%ux:%lux",ip->seg,ip->disp);
			break;
		case 'm':
			if (ip->mod == 3 && ip->osize != 'B') {
				if(fmt[1] != '*'){
					if(ip->opre != 0) {
						bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
						break;
					}
				} else
					fmt++;
				bprint(ip, "M%d", ip->base);
				break;
			}
			pea(ip);
			break;
		case 'e':
			pea(ip);
			break;
		case 'f':
			bprint(ip, "F%d", ip->base);
			break;
		case 'g':
			if (ip->reg < 6)
				bprint(ip,"%s",sreg[ip->reg]);
			else
				bprint(ip,"?");
			break;
		case 'p':
			/*
			 * signed immediate in the ulong ip->imm.
			 */
			v = (long)ip->imm;
			immediate(ip, v+ip->addr+ip->n);
			break;
		case 'r':
			if (ip->osize == 'B')
				bprint(ip,"%s", (ip->rex? breg64: breg)[ip->rex&REXR? ip->reg+8: ip->reg]);
			else
				bprint(ip, reg[ip->rex&REXR? ip->reg+8: ip->reg]);
			break;
		case 'w':
			if (ip->osize == 'Q' || ip->rex & REXW)
				bprint(ip, "CQO");
			else if (ip->osize == 'L')
				bprint(ip,"CDQ");
			else
				bprint(ip, "CWD");
			break;
		case 'M':
			if(ip->opre != 0)
				bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
			else
				bprint(ip, "M%d", ip->reg);
			break;
		case 'x':
			if (ip->mod == 3 && ip->osize != 'B') {
				bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
				break;
			}
			pea(ip);
			break;
		case 'X':
			bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
			break;
		default:
			bprint(ip, "%%%c", *fmt);
			break;
		}
	}
	*ip->curr = 0;		/* there's always room for 1 byte */
}

static int
i386inst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
	Instr instr;
	Optable *op;

	USED(modifier);
	op = mkinstr(map, &instr, pc);
	if (op == 0) {
		errstr(buf, n);
		return -1;
	}
	instr.curr = buf;
	instr.end = buf+n-1;
	prinstr(&instr, op->proto);
	return instr.n;
}

static int
i386das(Map *map, uvlong pc, char *buf, int n)
{
	Instr instr;
	int i;

	if (mkinstr(map, &instr, pc) == 0) {
		errstr(buf, n);
		return -1;
	}
	for(i = 0; i < instr.n && n > 2; i++) {
		_hexify(buf, instr.mem[i], 1);
		buf += 2;
		n -= 2;
	}
	*buf = 0;
	return instr.n;
}

static int
i386instlen(Map *map, uvlong pc)
{
	Instr i;

	if (mkinstr(map, &i, pc))
		return i.n;
	return -1;
}

static int
i386foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
	Instr i;
	Optable *op;
	ushort s;
	uvlong l, addr;
	vlong v;
	int n;

	op = mkinstr(map, &i, pc);
	if (!op)
		return -1;

	n = 0;

	switch(i.jumptype) {
	case RET:		/* RETURN or LEAVE */
	case Iw:		/* RETURN */
		if (strcmp(op->proto, "LEAVE") == 0) {
			if (geta(map, (*rget)(map, "BP"), &l) < 0)
				return -1;
		} else if (geta(map, (*rget)(map, mach->sp), &l) < 0)
			return -1;
		foll[0] = l;
		return 1;
	case Iwds:		/* pc relative JUMP or CALL*/
	case Jbs:		/* pc relative JUMP or CALL */
		v = (long)i.imm;
		foll[0] = pc+v+i.n;
		n = 1;
		break;
	case PTR:		/* seg:displacement JUMP or CALL */
		foll[0] = (i.seg<<4)+i.disp;
		return 1;
	case JUMP:		/* JUMP or CALL EA */

		if(i.mod == 3) {
			foll[0] = (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]);
			return 1;
		}
			/* calculate the effective address */
		addr = i.disp;
		if (i.base >= 0) {
			if (geta(map, (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]), &l) < 0)
				return -1;
			addr += l;
		}
		if (i.index >= 0) {
			if (geta(map, (*rget)(map, reg[i.rex&REXX? i.index+8: i.index]), &l) < 0)
				return -1;
			addr += l*(1<<i.ss);
		}
			/* now retrieve a seg:disp value at that address */
		if (get2(map, addr, &s) < 0)			/* seg */
			return -1;
		foll[0] = s<<4;
		addr += 2;
		if (i.asize == 'L') {
			if (geta(map, addr, &l) < 0)		/* disp32 */
				return -1;
			foll[0] += l;
		} else {					/* disp16 */
			if (get2(map, addr, &s) < 0)
				return -1;
			foll[0] += s;
		}
		return 1;
	default:
		break;
	}		
	if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
		return 1;
	foll[n++] = pc+i.n;
	return n;
}