// Inferno libmach/8db.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c
//
//	Copyright © 1994-1999 Lucent Technologies Inc.
//	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
//	Portions Copyright © 1997-1999 Vita Nuova Limited.
//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
//	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Ureg UregAmd64
#include <ureg_amd64.h>
#undef Ureg
#define Ureg Ureg386
#include <ureg_x86.h>
#undef Ureg

typedef struct UregAmd64 UregAmd64;
typedef struct Ureg386 Ureg386;

/*
 * 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	GOSTARTSYM[] =	"sys·goexit";
static	char	PROFSYM[] =	"_mainp";
static	char	FRAMENAME[] =	".frame";
static	char	LESSSTACK[] = "sys·lessstack";
static	char	MORESTACK[] = "sys·morestack";
static char *excname[] =
{
[0] =	"divide error",
[1] =	"debug exception",
[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",
[16] =	"math coprocessor error",
[17] =	"alignment check",
[18] =	"machine check",
[19] =	"floating-point exception",
[24] =	"clock",
[25] =	"keyboard",
[27] =	"modem status",
[28] =	"serial line status",
[30] =	"floppy disk",
[36] =	"mouse",
[37] =	"math coprocessor",
[38] =	"hard disk",
[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 int32 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)
{
	uint32 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 %d", c);
		return buf;
	} else
		return excname[c];
}

static int
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
	int i;
	uvlong osp, pc1;
	Symbol s, f, s1;
	extern Mach mamd64;
	int isamd64;
	uvlong g, m, lessstack, morestack, stktop;

	isamd64 = (mach == &mamd64);

	// ../pkg/runtime/runtime.h
	// G is
	//	byte* stackguard
	//	byte* stackbase (= Stktop*)
	// TODO(rsc): Need some way to get at the g for other threads.
	// Probably need to pass it into the trace function.
	g = 0;
	if(isamd64)
		geta(map, offsetof(struct UregAmd64, r15), &g);
	else {
		// TODO(rsc): How to fetch g on 386?
	}
	stktop = 0;
	if(g != 0)
		geta(map, g+1*mach->szaddr, &stktop);

	lessstack = 0;
	if(lookup(0, LESSSTACK, &s))
		lessstack = s.value;
	morestack = 0;
	if(lookup(0, MORESTACK, &s))
		morestack = s.value;

	USED(link);
	osp = 0;
	i = 0;

	for(;;) {
		if(!findsym(pc, CTEXT, &s)) {
			// check for closure return sequence
			uchar buf[8], *p;
			if(get1(map, pc, buf, 8) < 0)
				break;
			// ADDQ $xxx, SP; RET
			p = buf;
			if(mach == &mamd64) {
				if(p[0] != 0x48)
					break;
				p++;
			}
			if(p[0] != 0x81 || p[1] != 0xc4 || p[6] != 0xc3)
				break;
			sp += p[2] | (p[3]<<8) | (p[4]<<16) | (p[5]<<24);
			if(geta(map, sp, &pc) < 0)
				break;
			sp += mach->szaddr;
			continue;
		}

		if (osp == sp)
			break;
		osp = sp;

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

		if(s.value == morestack) {
			// This code is old and won't work anymore.
			// But no one uses it anyway.
			// Leave it obviously broken until someone needs it.
			werrstr("morestack not implemented correctly");
			return -1;
			// In the middle of morestack.
			// Caller is m->morepc.
			// Caller's caller is in m->morearg.
			// TODO(rsc): 386
			geta(map, offsetof(struct UregAmd64, r14), &m);

			pc = 0;
			sp = 0;
			pc1 = 0;
			s1 = s;
			memset(&s, 0, sizeof s);
			geta(map, m+1*mach->szaddr, &pc1);	// m->morepc
			geta(map, m+2*mach->szaddr, &sp);	// m->morebuf.sp
			geta(map, m+3*mach->szaddr, &pc);	// m->morebuf.pc
			findsym(pc1, CTEXT, &s);
			(*trace)(map, pc1, sp-mach->szaddr, &s1);	// morestack symbol; caller's PC/SP

			// caller's caller
			s1 = s;
			findsym(pc, CTEXT, &s);
			(*trace)(map, pc, sp, &s1);		// morestack's caller; caller's caller's PC/SP
			continue;
		}

		if(pc == lessstack) {
			// ../pkg/runtime/runtime.h
			// Stktop is
			//	byte* stackguard
			//	byte* stackbase
			//	Gobuf gobuf
			//		byte* sp;
			//		byte* pc;
			//		G*	g;
			if(!isamd64)
				fprint(2, "warning: cannot unwind stack split on 386\n");
			if(stktop == 0)
				break;
			pc = 0;
			sp = 0;
			geta(map, stktop+2*mach->szaddr, &sp);
			geta(map, stktop+3*mach->szaddr, &pc);
			geta(map, stktop+1*mach->szaddr, &stktop);
			(*trace)(map, pc, sp, &s1);
			continue;
		}

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

		// If PC is not valid, assume we caught the function
		// before it moved the stack pointer down or perhaps
		// after it moved the stack pointer back up.
		// Try the PC we'd have gotten without the stack
		// pointer adjustment above (pc != s.value).
		// This only matters for the first frame, and it is only
		// a heuristic, but it does help.
		if(!findsym(pc, CTEXT, &s) || strcmp(s.name, "etext") == 0)
			pc = pc1;

		if(pc == 0)
			break;

		if(pc != lessstack)
			(*trace)(map, pc, sp, &s1);
		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 */
	schar	index;		/* bits 3-5 of SIB */
	schar	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 */
	uint32	disp;		/* displacement */
	uint32 	imm;		/* immediate */
	uint32 	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 */
	/* be careful: some unix system headers #define R8, R9, etc */
	AMD64_R8,
	AMD64_R9,
	AMD64_R10,
	AMD64_R11,
	AMD64_R12,
	AMD64_R13,
	AMD64_R14,
	AMD64_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
{
	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 int32 R/M field with register (/r) */
	RMB,			/* Byte R/M field with register (/r) */
	RMOP,			/* Word or int32 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) */
	Op_R0,			/* Base reg of Mod R/M is literal 0x00 */
	Op_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" },
};

static Optable optab0F18[4]=
{
[0x00] =	{ 0,0,		"PREFETCHNTA	%e" },
[0x01] = 	{ 0,0,		"PREFECTCH0	%e" },
[0x02] = 	{ 0,0,		"PREFECTCH1	%e" },
[0x03] = 	{ 0,0,		"PREFECTCH2	%e" },
};

/* 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 optab660F38[256]=
{
[0x00] =	{ RM,0,		"PSHUFB	%x,%X" },
[0xdc] =	{ RM,0,		"AESENC	%x,%X" },
[0xdb] =	{ RM,0,		"AESIMC	%x,%X," },
[0xdd] =	{ RM,0,		"AESENCLAST	%x,%X" },
[0xde] =	{ RM,0,		"AESDEC	%x,%X" },
[0xdf] =	{ RM,0,		"AESDECLAST	%x,%X" },
};

static Optable optab660F3A[256]=
{
[0x22] =	{ RM,Ib,		"PINSR%S	%i,%e,%X" },
[0xdf] =	{ RM,Ib,		"AESKEYGENASSIST	%i,%x,%X" },
};

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" },
[0x38] =	{ AUX,0,		optab660F38 },
[0x3A] =	{ AUX,0,		optab660F3A },
[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 optabF20F38[256]=
{
[0xf0] =	{ RM,0,		"CRC32B	%e, %r" },
[0xf1] =	{ RM,0,		"CRC32%S	%e, %r" },
};

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" },
[0x38] =	{ AUX,0,		optabF20F38 },
[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" },
[0x18] =	{ RMOP,0,		optab0F18 },
[0x1F] =	{ RM,0,		"NOP%S	%e" },
[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" },
[0x77] =	{ 0,0,		"EMMS" },
[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	%x,%X,%#i" },
[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" },
[0x08] =	{ 0,0,		"FCMOVCS	%f,F0" },
[0x09] =	{ 0,0,		"FCMOVEQ	%f,F0" },
[0x0a] =	{ 0,0,		"FCMOVLS	%f,F0" },
[0x0b] =	{ 0,0,		"FCMOVUN	%f,F0" },
[0x0d] =	{ Op_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" },
[0x08] =	{ 0,0,		"FCMOVCC	F0,F0" },	/* Mod R/M = 11xx xxxx*/
[0x09] =	{ 0,0,		"FCMOVCC	F1,F0" },
[0x0a] =	{ 0,0,		"FCMOVCC	F2,F0" },
[0x0b] =	{ 0,0,		"FCMOVCC	F3,F0" },
[0x0c] =	{ 0,0,		"FCMOVCC	F4,F0" },
[0x0d] =	{ 0,0,		"FCMOVCC	F5,F0" },
[0x0e] =	{ 0,0,		"FCMOVCC	F6,F0" },
[0x0f] =	{ 0,0,		"FCMOVCC	F7,F0" },
[0x10] =	{ 0,0,		"FCMOVNE	F0,F0" },
[0x11] =	{ 0,0,		"FCMOVNE	F1,F0" },
[0x12] =	{ 0,0,		"FCMOVNE	F2,F0" },
[0x13] =	{ 0,0,		"FCMOVNE	F3,F0" },
[0x14] =	{ 0,0,		"FCMOVNE	F4,F0" },
[0x15] =	{ 0,0,		"FCMOVNE	F5,F0" },
[0x16] =	{ 0,0,		"FCMOVNE	F6,F0" },
[0x17] =	{ 0,0,		"FCMOVNE	F7,F0" },
[0x18] =	{ 0,0,		"FCMOVHI	F0,F0" },
[0x19] =	{ 0,0,		"FCMOVHI	F1,F0" },
[0x1a] =	{ 0,0,		"FCMOVHI	F2,F0" },
[0x1b] =	{ 0,0,		"FCMOVHI	F3,F0" },
[0x1c] =	{ 0,0,		"FCMOVHI	F4,F0" },
[0x1d] =	{ 0,0,		"FCMOVHI	F5,F0" },
[0x1e] =	{ 0,0,		"FCMOVHI	F6,F0" },
[0x1f] =	{ 0,0,		"FCMOVHI	F7,F0" },
[0x20] =	{ 0,0,		"FCMOVNU	F0,F0" },
[0x21] =	{ 0,0,		"FCMOVNU	F1,F0" },
[0x22] =	{ 0,0,		"FCMOVNU	F2,F0" },
[0x23] =	{ 0,0,		"FCMOVNU	F3,F0" },
[0x24] =	{ 0,0,		"FCMOVNU	F4,F0" },
[0x25] =	{ 0,0,		"FCMOVNU	F5,F0" },
[0x26] =	{ 0,0,		"FCMOVNU	F6,F0" },
[0x27] =	{ 0,0,		"FCMOVNU	F7,F0" },
[0x2a] =	{ 0,0,		"FCLEX" },
[0x2b] =	{ 0,0,		"FINIT" },
[0x30] =	{ 0,0,		"FUCOMI	F0,F0" },
[0x31] =	{ 0,0,		"FUCOMI	F1,F0" },
[0x32] =	{ 0,0,		"FUCOMI	F2,F0" },
[0x33] =	{ 0,0,		"FUCOMI	F3,F0" },
[0x34] =	{ 0,0,		"FUCOMI	F4,F0" },
[0x35] =	{ 0,0,		"FUCOMI	F5,F0" },
[0x36] =	{ 0,0,		"FUCOMI	F6,F0" },
[0x37] =	{ 0,0,		"FUCOMI	F7,F0" },
[0x38] =	{ 0,0,		"FCOMI	F0,F0" },
[0x39] =	{ 0,0,		"FCOMI	F1,F0" },
[0x3a] =	{ 0,0,		"FCOMI	F2,F0" },
[0x3b] =	{ 0,0,		"FCOMI	F3,F0" },
[0x3c] =	{ 0,0,		"FCOMI	F4,F0" },
[0x3d] =	{ 0,0,		"FCOMI	F5,F0" },
[0x3e] =	{ 0,0,		"FCOMI	F6,F0" },
[0x3f] =	{ 0,0,		"FCOMI	F7,F0" },
};

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] =	{ Op_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] =	{ Op_R0,0,		"FSTSW	%OAX" },
[0x0d] =	{ 0,0,		"FUCOMIP	F0,%f" },
[0x0e] =	{ 0,0,		"FCOMIP	F0,%f" },
};

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+2] =
{
[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	%e,%r" },
[0x101] =	{ RM,0,		"MOVLQZX	%e,%r" },
};

/*
 *  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, uint32 *lp)
{
	ushort s;
	int32	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)
{
	uint32	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];

	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){
			if(ip->rex&REXW)
				op = &obase[0x100];	/* MOVLQSX */
			else
				op = &obase[0x101];	/* MOVLQZX */
			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 = (int32)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 = (int32)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) {
					uint32 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 Op_R0:	/* base register must be R0 */
			if (ip->base != 0)
				goto badop;
			break;
		case Op_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;
				ip->prefix = 0; /* discard REPNE */
				break;
			case 0xF3:
				op = optabF30F;
				ip->prefix = 0; /* discard REP */
				break;
			default:
				op = nil;
				break;
			}
			if(op != nil && op[c].proto != nil)
				obase = op;
			/* 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;
			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] =	"AX",
[CX] =	"CX",
[DX] =	"DX",
[BX] =	"BX",
[SP] =	"SP",
[BP] =	"BP",
[SI] =	"SI",
[DI] =	"DI",

	/* amd64 */
[AMD64_R8] =	"R8",
[AMD64_R9] =	"R9",
[AMD64_R10] =	"R10",
[AMD64_R11] =	"R11",
[AMD64_R12] =	"R12",
[AMD64_R13] =	"R13",
[AMD64_R14] =	"R14",
[AMD64_R15] =	"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;
	int32 offset;
	Symbol s;
	char *reg;

	offset = ip->disp;
	if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
		bprint(ip, "%ux(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, "%ux%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, int32 w, int32 val)
{
	Symbol next, tmp;
	int32 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;
	int32 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+%#ux(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)
{
	int base;

	base = ip->base;
	if(base >= 0 && (ip->rex & REXB))
		base += 8;

	if (ip->mod == 3) {
		if (ip->osize == 'B')
			bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]);
		else
			bprint(ip, "%s%s", ANAME(ip), reg[base]);
		return;
	}

	if (ip->segment)
		bprint(ip, ip->segment);
	if (ip->asize == 'E' && base == SP)
		plocal(ip);
	else {
		if (ip->base < 0)
			immediate(ip, ip->disp);
		else {
			bprint(ip, "%ux", 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)
{
	int sharp;
	vlong v;

	if (ip->prefix)
		bprint(ip, "%s ", ip->prefix);
	for (; *fmt && ip->curr < ip->end; fmt++) {
		if (*fmt != '%'){
			*ip->curr++ = *fmt;
			continue;
		}
		sharp = 0;
		if(*++fmt == '#') {
			sharp = 1;
			++fmt;
		}
		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':
			if(!sharp)
				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:%ux", 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 uint32 ip->imm.
			 */
			v = (int32)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 = (int32)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;
}
