Plan 9 from Bell Labs’s /usr/web/sources/contrib/rsc/8dis.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>

/*
 * hacked i386-specific debugger interface
 */

	/* 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 */
	ulong	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	jumploc;
	char	osize;		/* 'W' or 'L' */
	char	asize;		/* address size 'W' or 'L' */
	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 */
	short	seg;		/* segment of far address */
	ulong	disp;		/* displacement */
	ulong 	imm;		/* immediate */
	ulong 	imm2;		/* second immediate operand */
	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,
};
	/* 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')
%X	-	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
%x	-	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)*/
	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 */
	PRE,			/* Instr Prefix */
	SEG,			/* Segment Prefix */
	OPOVER,			/* Operand size override */
	ADDOVER,		/* Address size override */

	JOUT,
	JBOTH,
};
	
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 */
};

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 optab0F[256]=
{
[0x00]	RMOP,0,		optab0F00,
[0x01]	RMOP,0,		optab0F01,
[0x02]	RM,0,		"LAR	%e,%r",
[0x03]	RM,0,		"LSL	%e,%r",
[0x06]	0,0,		"CLTS",
[0x08]	0,0,		"INVD",
[0x09]	0,0,		"WBINVD",
[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",
[0x30]	0,0,		"WRMSR",
[0x31]	0,0,		"RDTSC",
[0x32]	0,0,		"RDMSR",
[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 */
[0x80]	Iwds,JBOTH,		"JOS	%p",
[0x81]	Iwds,JBOTH,		"JOC	%p",
[0x82]	Iwds,JBOTH,		"JCS	%p",
[0x83]	Iwds,JBOTH,		"JCC	%p",
[0x84]	Iwds,JBOTH,		"JEQ	%p",
[0x85]	Iwds,JBOTH,		"JNE	%p",
[0x86]	Iwds,JBOTH,		"JLS	%p",
[0x87]	Iwds,JBOTH,		"JHI	%p",
[0x88]	Iwds,JBOTH,		"JMI	%p",
[0x89]	Iwds,JBOTH,		"JPL	%p",
[0x8a]	Iwds,JBOTH,		"JPS	%p",
[0x8b]	Iwds,JBOTH,		"JPC	%p",
[0x8c]	Iwds,JBOTH,		"JLT	%p",
[0x8d]	Iwds,JBOTH,		"JGE	%p",
[0x8e]	Iwds,JBOTH,		"JLE	%p",
[0x8f]	Iwds,JBOTH,		"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",
};

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*%S %e",
[0x03]	JUMP,0,		"CALLF*%S %e",
[0x04]	JUMP,0,		"JMP*%S	%e",
[0x05]	JUMP,0,		"JMPF*%S %e",
[0x06]	0,0,		"PUSHL	%e",
};

static Optable optable[256] =
{
[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]	AUX,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,JBOTH,		"JOS	%p",
[0x71]	Jbs,JBOTH,		"JOC	%p",
[0x72]	Jbs,JBOTH,		"JCS	%p",
[0x73]	Jbs,JBOTH,		"JCC	%p",
[0x74]	Jbs,JBOTH,		"JEQ	%p",
[0x75]	Jbs,JBOTH,		"JNE	%p",
[0x76]	Jbs,JBOTH,		"JLS	%p",
[0x77]	Jbs,JBOTH,		"JHI	%p",
[0x78]	Jbs,JBOTH,		"JMI	%p",
[0x79]	Jbs,JBOTH,		"JPL	%p",
[0x7a]	Jbs,JBOTH,		"JPS	%p",
[0x7b]	Jbs,JBOTH,		"JPC	%p",
[0x7c]	Jbs,JBOTH,		"JLT	%p",
[0x7d]	Jbs,JBOTH,		"JGE	%p",
[0x7e]	Jbs,JBOTH,		"JLE	%p",
[0x7f]	Jbs,JBOTH,		"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	%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,		"%X",			/* miserable CBW or CWDE */
[0x99]	0,0,		"%x",			/* idiotic CWD or CDQ */
[0x9a]	PTR,JBOTH,		"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]	Iwd,0,		"MOV%S	%i,%OAX",
[0xb9]	Iwd,0,		"MOV%S	%i,%OCX",
[0xba]	Iwd,0,		"MOV%S	%i,%ODX",
[0xbb]	Iwd,0,		"MOV%S	%i,%OBX",
[0xbc]	Iwd,0,		"MOV%S	%i,%OSP",
[0xbd]	Iwd,0,		"MOV%S	%i,%OBP",
[0xbe]	Iwd,0,		"MOV%S	%i,%OSI",
[0xbf]	Iwd,0,		"MOV%S	%i,%ODI",
[0xc0]	RMOPB,0,	optabC0,
[0xc1]	RMOP,0,		optabC1,
[0xc2]	Iw,JOUT,		"RET	%i",
[0xc3]	RET,JOUT,		"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]	0,0,		"LEAVE",		/* bizarre LEAVE */
[0xca]	Iw,JOUT,		"RETF	%i",
[0xcb]	RET,JOUT,		"RETF",
[0xcc]	0,0,		"INT	3",
[0xcd]	Ib,0,		"INTB	%i",
[0xce]	0,0,		"INTO",
[0xcf]	JOUT,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,JBOTH,		"LOOPNE	%p",
[0xe1]	Jbs,JBOTH,		"LOOPE	%p",
[0xe2]	Jbs,JBOTH,		"LOOP	%p",
[0xe3]	Jbs,JBOTH,		"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,JBOTH,		"CALL	%p",
[0xe9]	Iwds,JOUT,		"JMP	%p",
[0xea]	PTR,JOUT,		"JMP	%d",
[0xeb]	Jbs,JOUT,		"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]	PRE,0,		"REPNE",
[0xf3]	PRE,0,		"REP",
[0xf4]	0,0,		"HALT",
[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,
};

char *
_hexify(char *buf, ulong p, int zeros)
{
	ulong d;

	d = p/16;
	if(d)
		buf = _hexify(buf, d, zeros-1);
	else
		while(zeros--)
			*buf++ = '0';
	*buf++ = "0123456789abcdef"[p&0x0f];
	return buf;
}

/*
 *  get a byte of the instruction
 */
static int
igetc(Instr *ip, uchar *c)
{
	if(ip->n+1 > sizeof(ip->mem)){
		werrstr("instruction too long");
		return -1;
	}
	ip->mem[ip->n++] = *c = *(((uchar*)ip->addr)+ip->n);
	return 1;
}

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

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

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

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

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

	if (mod > 2)
		return 1;
	if (mod == 1) {
		if (igetc(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(ip, &ip->disp) < 0)
				return -1;
		} else {
			if (igets( 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(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;
	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(ip, mod, rm, 6);
	}
	if (rm == 4) {	/* scummy sib byte */
		if (igetc( 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(ip, mod, ip->base, 5);
	}
	return getdisp(ip, mod, rm, 5);
}

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

	memset(ip, 0, sizeof(*ip));
	ip->base = -1;
	ip->index = -1;
	if(1 /*asstype == AI8086 */)
		ip->osize = 'W';
	else {
		ip->osize = 'L';
		ip->asize = 'E';
	}
	ip->addr = (ulong) pc;
	if (igetc(ip, &c) < 0)
		return 0;
	obase = optable;
newop:
	op = &obase[c];
	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( ip, &c) < 0)
				return 0;
			ip->imm = c&0xff;
			break;
		case Jbs:	/* 8-bit jump immediate (sign extended) */
			if (igetc(ip, &c) < 0)
				return 0;
			if (c&0x80)
				ip->imm = c|0xffffff00;
			else
				ip->imm = c&0xff;
			ip->jumptype = Jbs;
			break;
		case Ibs:	/* 8-bit immediate (sign extended) */
			if (igetc(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;
			break;
		case Iw:	/* 16-bit immediate -> imm */
			if (igets(ip, &s) < 0)
				return 0;
			ip->imm = s&0xffff;
			ip->jumptype = Iw;
			break;
		case Iw2:	/* 16-bit immediate -> in imm2*/
			if (igets(ip, &s) < 0)
				return 0;
			ip->imm2 = s&0xffff;
			break;
		case Iwd:	/* Operand-sized immediate (no sign extension)*/
			if (ip->osize == 'L') {
				if (igetl(ip, &ip->imm) < 0)
					return 0;
			} else {
				if (igets(ip, &s)< 0)
					return 0;
				ip->imm = s&0xffff;
			}
			break;
		case Awd:	/* Address-sized immediate (no sign extension)*/
			if (ip->asize == 'E') {
				if (igetl(ip, &ip->imm) < 0)
					return 0;
			} else {
				if (igets(ip, &s)< 0)
					return 0;
				ip->imm = s&0xffff;
			}
			break;
		case Iwds:	/* Operand-sized immediate (sign extended) */
			if (ip->osize == 'L') {
				if (igetl(ip, &ip->imm) < 0)
					return 0;
			} else {
				if (igets(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(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(ip, &c) < 0)
				return 0;
			if (modrm(ip, c) < 0)
				return 0;
			ip->osize = 'B';
			break;
		case RM:	/* R/M field with register (/r) */
			if (igetc(ip, &c) < 0)
				return 0;
			if (modrm(ip, c) < 0)
				return 0;
			break;
		case RMOPB:	/* R/M field with op code (/digit) */
			if (igetc(ip, &c) < 0)
				return 0;
			if (modrm(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(ip, &c) < 0)
				return 0;
			if (modrm(ip, c) < 0)
				return 0;
			c = ip->reg;
			obase = (Optable*)op->proto;
			goto newop;
		case FRMOP:	/* FP R/M field with op code (/digit) */
			if (igetc(ip, &c) < 0)
				return 0;
			if (modrm(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(ip, &c) < 0)
				return 0;
			if (modrm(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(ip, &c) < 0)
				return 0;
			if ((c&0xc0) != 0xc0) {
				werrstr("invalid R/M register: %x", c);
				return 0;
			}
			if (modrm(ip, c) < 0)
				return 0;
			break;
		case RMM:	/* R/M register only (mod = 11) */
			if (igetc(ip, &c) < 0)
				return 0;
			if ((c&0xc0) == 0xc0) {
				werrstr("invalid R/M memory mode: %x", c);
				return 0;
			}
			if (modrm(ip, c) < 0)
				return 0;
			break;
		case PTR:	/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
			if (ip->osize == 'L') {
				if (igetl(ip, &ip->disp) < 0)
					return 0;
			} else {
				if (igets(ip, &s)< 0)
					return 0;
				ip->disp = s&0xffff;
			}
			if (igets(ip, (ushort*)&ip->seg) < 0)
				return 0;
			ip->jumptype = PTR;
			break;
		case AUX:	/* Multi-byte op code - Auxiliary table */
			obase = (Optable*)op->proto;
			if (igetc(ip, &c) < 0)
				return 0;
			goto newop;
		case PRE:	/* Instr Prefix */
			ip->prefix = (char*)op->proto;
			if (igetc(ip, &c) < 0)
				return 0;
			goto newop;
		case SEG:	/* Segment Prefix */
			ip->segment = (char*)op->proto;
			if (igetc(ip, &c) < 0)
				return 0;
			goto newop;
		case OPOVER:	/* Operand size override */
			ip->osize = 'W';
			if (igetc(ip, &c) < 0)
				return 0;
			goto newop;
		case ADDOVER:	/* Address size override */
			ip->asize = 0;
			if (igetc(ip, &c) < 0)
				return 0;
			goto newop;
		case JUMP:	/* mark instruction as JUMP or RET */
		case RET:
			ip->jumptype = op->operand[i];
			break;
		case JBOTH:
		case JOUT:
			ip->jumploc = op->operand[i];
			break;
		default:
			werrstr("bad operand type %d", op->operand[i]);
			return 0;
		}
	}
	return op;
}

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

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

/*
 *	if 32-bit registers are to be named E<rr>, macro ANAME should
 *	always return ip->asize and ONAME should return a 'E' when
 *	when ip->osize == 'L'.  The macros should return a null character
 *	when 32-bit registers are ddesignated by <rr>.
 */
#define	ANAME(ip)	ip->asize
#define	ONAME(ip)	((ip)->osize == 'L' ? 'E' : 0)

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

static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };

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

	offset = ip->disp;
//	if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
		bprint(ip, "%lx(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 void
pea(Instr *ip)
{
	if (ip->mod == 3) {
		if (ip->osize == 'B')
			bprint(ip, breg[ip->base]);
		else
			bprint(ip, "%c%s", ANAME(ip), reg[ip->base]);
		return;
	}
	if (ip->segment)
		bprint(ip, ip->segment);
	if (ip->asize == 'E' && ip->base == SP)
		plocal(ip);
	else {
		bprint(ip,"%lx", ip->disp);
		if (ip->base >= 0)
			bprint(ip,"(%c%s)", ANAME(ip), reg[ip->base]);
		else
			bprint(ip, "()");
	}
	if (ip->index >= 0)
		bprint(ip,"(%c%s*%d)", ANAME(ip), reg[ip->index], 1<<ip->ss);
}


int findsym(long, char**, long*);

static void
immediate(Instr *ip, long val)
{
/*
	char *s;
	long w, value;

	if(findsym(val, &s, &value)){
		w = val - s.value;
		if (w < 0)
			w = -w;
		if (w < 4096) {
			if (w)
				bprint(ip, "%s+%lux", s.name, w);
			else
				bprint(ip, "%s", s.name);
			return;
		}
	}
*/
	bprint(ip, "%lx", val);
}

long irel, offset;

static void
prinstr(Instr *ip, char *fmt)
{
	if (ip->prefix)
		bprint(ip, "%s ", ip->prefix);
	for (; *fmt && ip->curr < ip->end; fmt++) {
		if (*fmt != '%')
			*ip->curr++ = *fmt;
		else switch(*++fmt)
		{
		case '%':
			*ip->curr++ = '%';
			break;
		case 'A':
			bprint(ip, "%c", 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,"%c", ONAME(ip));
			break;
		case 'i':
			bprint(ip, "$");
			immediate(ip,ip->imm);
			break;
		case 'R':
			bprint(ip, "%c%s", ONAME(ip), reg[ip->reg]);
			break;
		case 'S':
			bprint(ip, "%c", ip->osize);
			break;
		case 'T':
			if (ip->reg == 6 || ip->reg == 7)
				bprint(ip, "TR%d",ip->reg);
			else
				bprint(ip, "???");
			break;
		case 'X':
			if (ip->osize == 'L')
				bprint(ip,"CWDE");
			else
				bprint(ip, "CBW");
			break;
		case 'd':
			bprint(ip,"%lux:%lux",ip->seg,ip->disp);
			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':
			immediate(ip, ip->imm+ip->addr+ip->n-irel+offset);
			break;
		case 'r':
			if (ip->osize == 'B')
				bprint(ip,"%s",breg[ip->reg]);
			else
				bprint(ip, reg[ip->reg]);
			break;
		case 'x':
			if (ip->osize == 'L')
				bprint(ip,"CDQ");
			else
				bprint(ip, "CWD");
			break;
		default:
			bprint(ip, "%%%c", *fmt);
			break;
		}
	}
	*ip->curr = 0;		/* there's always room for 1 byte */
}

int
i386das(uchar* pc, char *buf, int n)
{
	Instr	instr;
	Optable *op;

	op = mkinstr( &instr, pc);
	if (op == 0) {
		if (n >= ERRLEN)
			errstr(buf);
		else
			snprint(buf,n,"%r");
		return -1;
	}
	instr.curr = buf;
	instr.end = buf+n-1;
	prinstr(&instr, op->proto);
	return instr.n;
}

int
i386hexinst(uchar* pc, char *buf, int n)
{
	Instr	instr;
	int i;

	if (mkinstr( &instr, pc) == 0) {
		if (n >= ERRLEN)
			errstr(buf);
		else
			snprint(buf,n,"%r");
		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;
}

int
i386instlen(uchar* pc)
{
	Instr i;

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

void
disasm(uchar *v, int start, int nv)
{
	int i, n;
	char buf[100];

	for(n=start; n<nv; n+=i) {
		i = i386das(v+n, buf, sizeof buf);
		if(i <= 0) {
			print("fail %r\n");
			break;
		}
		print("%x+%x %s\n", n, i, buf);
	}
}

enum {
	Tunknown,
	Tstring,
	Tbyte,
	Tword,
	Tlong,
	Tinst,
};

#pragma varargck type "P" void*

int start;
int interactive;
uchar *code, *type;
int ncode;
int cs, ce;

void
change(int a)
{
	if(cs == ce){
		cs = a;
		ce = a+1;
		return;
	}
	if(cs > a)
		cs = a;
	if(ce <= a)
		ce = a+1;
}

void
explore(void)
{
	int i, j, ji, lcs, lce;
	Instr instr;

	while(cs != ce){ 
		lcs = cs;
		lce = ce;
		cs = ce = 0;
		for(i=lcs; i<lce; i++){
			if(type[i] != Tinst)
				continue;
			if(mkinstr(&instr, code+i) == nil)
				continue;
			for(j=0; j<instr.n; j++)
				if(type[i] != 0 && type[i] != Tinst)
					break;
			if(j != instr.n)
				continue;

			switch(instr.jumploc){
			case 0:
			case JBOTH:
				if(i+instr.n < ncode && type[i+instr.n] == 0){
					type[i+instr.n] = Tinst;
					if(i+instr.n >= lce)
						change(i+instr.n);
				}
				if(instr.jumploc == 0)
					break;
			case JOUT:
				ji = instr.imm+instr.addr+instr.n-irel;
				if(0 <= ji && ji < ncode && type[ji] == 0){
					type[ji] = Tinst;
					if(ji < i || lce <= ji)
						change(ji);
				}
				break;
			}
		}
	}
}

int
Pconv(va_list *arg, Fconv *f)
{
	char buf[10];
	int i, n;
	uchar *p;

	p = va_arg(*arg, uchar*);
	n = f->f2;
	if(n > 0){
		f->f2 = -1000;
		for(i=0; i<n; i++){
			sprint(buf, "%2.2uX", p[i]);
			strconv(buf, f);
		}
		for(; n*2 < 16; n++)
			strconv("  ", f);
	}

	return 0;
}

static int partial, bstart;
Biobuf bout;

void
endbyte(void)
{
	int j;

	if(partial){
		for(j=partial; j%16; j++)
			Bprint(&bout, "   ");
		for(j=partial&~15; j<bstart; j++)
			Bprint(&bout, " ");
		for(; j<partial; j++){
			if(isprint(code[j]))
				Bprint(&bout, "%c", code[j]);
			else
				Bprint(&bout, ".");
		}
		for(; j%16; j++)
			Bprint(&bout, " ");
		Bprint(&bout, "\n");
		partial = 0;
	}
}

void
printbyte(int i)
{
	int j;

	if(partial != 0 && partial != i){
		endbyte();
		partial = 0;
	}
	if(partial == 0){
		bstart = i;
		partial = i;
		Bprint(&bout, "%.4lX\t", i+offset);
		for(j=i&~15; j<i; j++)
			Bprint(&bout, "   ");
	}
	Bprint(&bout, "%2.2uX ", code[i]);
	partial++;
	if(partial%16 == 0){
		for(j=bstart; j<partial; j++){
			if(isprint(code[j]))
				Bprint(&bout, "%c", code[j]);
			else
				Bprint(&bout, ".");
		}
		Bprint(&bout, "\n");
		Bprint(&bout, "%.4lX\t", (partial&~15)+offset);
		bstart = partial;
	}
}

void
dumpasm(void)
{
	int i, j;
	Instr instr;
	Optable *op;
	char buf[128];

	Binit(&bout, 1, OWRITE);
	for(i=0; i<ncode; ){
		switch(type[i]){
		case Tstring:
			for(j=i; j<ncode && code[j]; j++)
				if(type[i] != 0 && type[i] != Tstring)
					goto Default;
			if(j==ncode)
				goto Default;
			endbyte();
			Bprint(&bout, "%.4lX\t\"%s\"\n", i+offset, (char*)code+i);
			i=j+1;
			break;
		case Tinst:
			if(op = mkinstr(&instr, code+i)){
				for(j=0; j<instr.n; j++)
					if(type[i] != 0 && type[i] != Tinst)
						goto Default;
				instr.curr = buf;
				instr.end = buf+sizeof(buf)-1;
				prinstr(&instr, op->proto);
				endbyte();
				Bprint(&bout, "%.4lX\t%.*P\t%s\n", i+offset, instr.n, code+i, buf);
				i += instr.n;
				break;
			}
		Default:
		default:
			printbyte(i);
			i++;
			break;
		}
	}
	endbyte();
	Bprint(&bout, "\n");
	Bflush(&bout);
}

void
usage(void)
{
	fprint(2, "usage: 8dis [-i] [-s start-offset] file\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	int fd;
	char buf[128];
	Dir d;

	fmtinstall('P', Pconv);
	ARGBEGIN{
	case 'i':
		interactive++;
		break;
	case 'o':
		offset = strtol(EARGF(usage()), 0, 0);
		break;
	case 's':
		start = strtol(EARGF(usage()), 0, 0);
		break;
	default:
		usage();
	}ARGEND

	if(argc != 1)
		usage();

	if((fd = open(argv[0], OREAD)) < 0) {
		fprint(2, "cannot open %s: %r\n", argv[0]);
		exits("open");
	}

	if(dirfstat(fd, &d) < 0) {
		fprint(2, "dirfstat: %r\n");
		exits("dirfstat");
	}

	ncode = d.length;
	code = malloc(ncode);
	assert(code != nil);

	type = malloc(ncode);
	assert(type != nil);
	memset(type, 0, ncode);
	irel = (long)code;

	if(readn(fd, code, ncode) != ncode) {
		fprint(2, "read error: %r\n");
		exits("read");
	}

	if(interactive){
		char *p, *f[10];
		int addr, lineno, nf, t;
		Biobuf b;

		lineno = 0;
		Binit(&b, 0, OREAD);
		while(p = Brdline(&b, '\n')){
			lineno++;
			p[Blinelen(&b)-1] = '\0';
			if(p[0] == '#')
				continue;
			strecpy(buf, buf+sizeof buf, p);
			nf = tokenize(p, f, nelem(f));
			if(nf == 0)
				continue;
			switch(f[0][0]){
			case 't':	/* tc addr: addr is type c */
				if(nf < 2 || f[0][1] == '\0'){
					print("\"%s\": usage: tc addr [name]\n", buf);
					continue;
				}
				addr = atoi(f[1])-offset;
				if(addr < 0 || addr >= ncode){
					print("\"%s\": bad addr %#x\n", buf, addr);
					continue;
				}
				switch(f[0][1]){
				case 's':	t = Tstring;	break;
				case 'b':	t = Tbyte;		break;
				case 'w':	t = Tword;		break;
				case 'l':	t = Tlong;		break;
				case 'i':	t = Tinst;		break;
				default:
					print("\"%s\": bad type '%c'\n", buf, f[0][1]);
					continue;
				}
				type[addr] = t;
				change(addr);
				break;
			}
		}
	}
	if(start || cs==ce){
		type[start] = Tinst;
		change(start);
	}
	explore();
	dumpasm();
	exits(0);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.