// SPDX-FileCopyrightText: 2008-2020 pancake
// SPDX-FileCopyrightText: 2008-2020 unlogic
// SPDX-FileCopyrightText: 2008-2020 emvivre
// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_flag.h>
#include <rz_core.h>
#include <rz_asm.h>
#include <rz_lib.h>
#include <rz_types.h>
#include <stdio.h>
#include <string.h>

static ut64 getnum(RzAsm *a, const char *s);

#define ENCODING_SHIFT 0
#define OPTYPE_SHIFT   6
#define REGMASK_SHIFT  16
#define OPSIZE_SHIFT   24

// How to encode the operand?
#define OT_REGMEM     (1 << (ENCODING_SHIFT + 0))
#define OT_SPECIAL    (1 << (ENCODING_SHIFT + 1))
#define OT_IMMEDIATE  (1 << (ENCODING_SHIFT + 2))
#define OT_JMPADDRESS (1 << (ENCODING_SHIFT + 3))

// Register indices - by default, we allow all registers
#define OT_REGALL (0xff << REGMASK_SHIFT)

// Memory or register operands: how is the operand written in assembly code?
#define OT_MEMORY     (1 << (OPTYPE_SHIFT + 0))
#define OT_CONSTANT   (1 << (OPTYPE_SHIFT + 1))
#define OT_GPREG      ((1 << (OPTYPE_SHIFT + 2)) | OT_REGALL)
#define OT_SEGMENTREG ((1 << (OPTYPE_SHIFT + 3)) | OT_REGALL)
#define OT_FPUREG     ((1 << (OPTYPE_SHIFT + 4)) | OT_REGALL)
#define OT_MMXREG     ((1 << (OPTYPE_SHIFT + 5)) | OT_REGALL)
#define OT_XMMREG     ((1 << (OPTYPE_SHIFT + 6)) | OT_REGALL)
#define OT_CONTROLREG ((1 << (OPTYPE_SHIFT + 7)) | OT_REGALL)
#define OT_DEBUGREG   ((1 << (OPTYPE_SHIFT + 8)) | OT_REGALL)
#define OT_SREG       ((1 << (OPTYPE_SHIFT + 9)) | OT_REGALL)
// more?

#define OT_REGTYPE ((OT_GPREG | OT_SEGMENTREG | OT_FPUREG | OT_MMXREG | OT_XMMREG | OT_CONTROLREG | OT_DEBUGREG) & ~OT_REGALL)

// Register mask
#define OT_REG(num) ((1 << (REGMASK_SHIFT + (num))) | OT_REGTYPE)

#define OT_UNKNOWN (0 << OPSIZE_SHIFT)
#define OT_BYTE    (1 << OPSIZE_SHIFT)
#define OT_WORD    (2 << OPSIZE_SHIFT)
#define OT_DWORD   (4 << OPSIZE_SHIFT)
#define OT_QWORD   (8 << OPSIZE_SHIFT)
#define OT_OWORD   (16 << OPSIZE_SHIFT)
#define OT_TBYTE   (32 << OPSIZE_SHIFT)

#define ALL_SIZE (OT_BYTE | OT_WORD | OT_DWORD | OT_QWORD | OT_OWORD)

// For register operands, we mostl don't care about the size.
// So let's just set all relevant flags.
#define OT_FPUSIZE (OT_DWORD | OT_QWORD | OT_TBYTE)
#define OT_XMMSIZE (OT_DWORD | OT_QWORD | OT_OWORD)

// Macros for encoding
#define OT_REGMEMOP(type)  (OT_##type##REG | OT_MEMORY | OT_REGMEM)
#define OT_REGONLYOP(type) (OT_##type##REG | OT_REGMEM)
#define OT_MEMONLYOP       (OT_MEMORY | OT_REGMEM)
#define OT_MEMIMMOP        (OT_MEMORY | OT_IMMEDIATE)
#define OT_REGSPECOP(type) (OT_##type##REG | OT_SPECIAL)
#define OT_IMMOP           (OT_CONSTANT | OT_IMMEDIATE)
#define OT_MEMADDROP       (OT_MEMORY | OT_IMMEDIATE)

// Some operations are encoded via opcode + spec field
#define SPECIAL_SPEC 0x00010000
#define SPECIAL_MASK 0x00000007

#define MAX_OPERANDS     3
#define MAX_REPOP_LENGTH 20

#define is_valid_registers(op) \
	if (is_debug_or_control(op->operands[0]) || is_debug_or_control(op->operands[1])) \
		return -1;

const ut8 SEG_REG_PREFIXES[] = { 0x26, 0x2e, 0x36, 0x3e, 0x64, 0x65 };

typedef enum tokentype_t {
	TT_EOF,
	TT_WORD,
	TT_NUMBER,
	TT_SPECIAL
} x86newTokenType;

typedef enum register_t {
	X86R_UNDEFINED = -1,
	X86R_EAX = 0,
	X86R_ECX,
	X86R_EDX,
	X86R_EBX,
	X86R_ESP,
	X86R_EBP,
	X86R_ESI,
	X86R_EDI,
	X86R_EIP,
	X86R_AX = 0,
	X86R_CX,
	X86R_DX,
	X86R_BX,
	X86R_SP,
	X86R_BP,
	X86R_SI,
	X86R_DI,
	X86R_AL = 0,
	X86R_CL,
	X86R_DL,
	X86R_BL,
	X86R_AH,
	X86R_CH,
	X86R_DH,
	X86R_BH,
	X86R_RAX = 0,
	X86R_RCX,
	X86R_RDX,
	X86R_RBX,
	X86R_RSP,
	X86R_RBP,
	X86R_RSI,
	X86R_RDI,
	X86R_RIP,
	X86R_R8 = 0,
	X86R_R9,
	X86R_R10,
	X86R_R11,
	X86R_R12,
	X86R_R13,
	X86R_R14,
	X86R_R15,
	X86R_CS = 0,
	X86R_SS,
	X86R_DS,
	X86R_ES,
	X86R_FS,
	X86R_GS, // Is this the right order?
	X86R_CR0 = 0,
	X86R_CR1,
	X86R_CR2,
	X86R_CR3,
	X86R_CR4,
	X86R_CR5,
	X86R_CR6,
	X86R_CR7,
	X86R_DR0 = 0,
	X86R_DR1,
	X86R_DR2,
	X86R_DR3,
	X86R_DR4,
	X86R_DR5,
	X86R_DR6,
	X86R_DR7
} Register;

typedef struct operand_t {
	ut32 type;
	st8 sign;
	struct {
		Register reg;
		bool extended;
	};
	union {
		struct {
			ut64 offset;
			st8 offset_sign;
			Register regs[2];
			int scale[2];
		};
		struct {
			ut64 immediate;
			bool is_good_flag;
		};
		struct {
			char rep_op[MAX_REPOP_LENGTH];
		};
	};
	bool explicit_size;
	ut32 dest_size;
	ut32 reg_size;
} Operand;

typedef struct Opcode_t {
	char *mnemonic;
	ut32 op[3];
	size_t op_len;
	bool is_short;
	ut8 opcode[3];
	int operands_count;
	Operand operands[MAX_OPERANDS];
	bool has_bnd;
} Opcode;

static inline bool is_debug_or_control(Operand op) {
	return (op.type & OT_REGTYPE) & (OT_CONTROLREG | OT_DEBUGREG);
}

static ut8 getsib(const ut8 sib) {
	if (!sib) {
		return 0;
	}
	return (sib & 0x8) ? 3 : getsib((sib << 1) | 1) - 1;
}

static int is_al_reg(const Operand *op) {
	if (op->type & OT_MEMORY) {
		return 0;
	}
	if (op->reg == X86R_AL && op->type & OT_BYTE) {
		return 1;
	}
	return 0;
}

static int oprep(RzAsm *a, ut8 *data, const Opcode *op);

static int process_16bit_group_1(RzAsm *a, ut8 *data, const Opcode *op, int op1) {
	is_valid_registers(op);
	int l = 0;
	int immediate = op->operands[1].immediate * op->operands[1].sign;

	data[l++] = 0x66;
	if (op->operands[1].immediate < 128) {
		data[l++] = 0x83;
		data[l++] = op->operands[0].reg | (0xc0 + op1 + op->operands[0].reg);
	} else {
		if (op->operands[0].reg == X86R_AX) {
			data[l++] = 0x05 + op1;
		} else {
			data[l++] = 0x81;
			data[l++] = (0xc0 + op1) | op->operands[0].reg;
		}
	}
	data[l++] = immediate;
	if (op->operands[1].immediate > 127) {
		data[l++] = immediate >> 8;
	}

	return l;
}

static int process_group_1(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int modrm = 0;
	int mod_byte = 0;
	int offset = 0;
	int mem_ref = 0;
	st32 immediate = 0;

	if (!op->operands[1].is_good_flag) {
		return -1;
	}
	if (a->bits == 64 && op->operands[0].type & OT_QWORD) {
		data[l++] = op->operands[0].extended ? 0x49 : 0x48;
	}
	if (!strcmp(op->mnemonic, "adc")) {
		modrm = 2;
	} else if (!strcmp(op->mnemonic, "add")) {
		modrm = 0;
	} else if (!strcmp(op->mnemonic, "or")) {
		modrm = 1;
	} else if (!strcmp(op->mnemonic, "and")) {
		modrm = 4;
	} else if (!strcmp(op->mnemonic, "xor")) {
		modrm = 6;
	} else if (!strcmp(op->mnemonic, "sbb")) {
		modrm = 3;
	} else if (!strcmp(op->mnemonic, "sub")) {
		modrm = 5;
	} else if (!strcmp(op->mnemonic, "cmp")) {
		modrm = 7;
	}
	immediate = op->operands[1].immediate * op->operands[1].sign;

	if (op->operands[0].type & OT_DWORD ||
		op->operands[0].type & OT_QWORD) {
		if (op->operands[1].immediate < 128) {
			data[l++] = 0x83;
		} else if (op->operands[0].reg != X86R_EAX ||
			op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x81;
		}
	} else if (op->operands[0].type & OT_BYTE) {
		if (op->operands[1].immediate > 255) {
			RZ_LOG_ERROR("assembler: x86.nz: %s: the immediate value exceeds bounds (imm > 255)\n", op->mnemonic);
			return -1;
		}
		data[l++] = 0x80;
	}
	if (op->operands[0].type & OT_MEMORY) {
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (op->operands[0].offset || op->operands[0].regs[0] == X86R_EBP) {
			mod_byte = 1;
		}
		if (offset < ST8_MIN || offset > ST8_MAX) {
			mod_byte = 2;
		}
		int reg0 = op->operands[0].regs[0];
		if (reg0 == -1) {
			mem_ref = 1;
			reg0 = 5;
			mod_byte = 0;
		}
		data[l++] = mod_byte << 6 | modrm << 3 | reg0;
		if (op->operands[0].regs[0] == X86R_ESP) {
			data[l++] = 0x24;
		}
		if (mod_byte || mem_ref) {
			data[l++] = offset;
			if (mod_byte == 2 || mem_ref) {
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
		}
	} else {
		if (op->operands[1].immediate > 127 && op->operands[0].reg == X86R_EAX) {
			data[l++] = 5 | modrm << 3 | op->operands[0].reg;
		} else {
			mod_byte = 3;
			data[l++] = mod_byte << 6 | modrm << 3 | op->operands[0].reg;
		}
	}

	data[l++] = immediate;
	if ((immediate > 127 || immediate < -128) &&
		((op->operands[0].type & OT_DWORD) || (op->operands[0].type & OT_QWORD))) {
		data[l++] = immediate >> 8;
		data[l++] = immediate >> 16;
		data[l++] = immediate >> 24;
	}
	return l;
}

static int process_group_2(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int modrm = 0;
	int mod_byte = 0;
	int reg0 = 0;

	if (a->bits == 64 && op->operands[0].type & OT_QWORD) {
		data[l++] = 0x48;
	}

	if (!strcmp(op->mnemonic, "rol")) {
		modrm = 0;
	} else if (!strcmp(op->mnemonic, "ror")) {
		modrm = 1;
	} else if (!strcmp(op->mnemonic, "rcl")) {
		modrm = 2;
	} else if (!strcmp(op->mnemonic, "rcr")) {
		modrm = 3;
	} else if (!strcmp(op->mnemonic, "shl")) {
		modrm = 4;
	} else if (!strcmp(op->mnemonic, "shr")) {
		modrm = 5;
	} else if (!strcmp(op->mnemonic, "sal")) {
		modrm = 6;
	} else if (!strcmp(op->mnemonic, "sar")) {
		modrm = 7;
	}

	st32 immediate = op->operands[1].immediate * op->operands[1].sign;
	if (immediate > 255 || immediate < -128) {
		RZ_LOG_ERROR("assembler: x86.nz: %s: the immediate value exceeds bounds (imm > 255 or imm < -128)\n", op->mnemonic);
		return -1;
	} else if (!strcmp(op->mnemonic, "rcl") && !immediate) {
		// `rcl mem [reg]` is an alias for `rcl mem [reg], 1`
		immediate = 1;
	}

	if (op->operands[0].type & (OT_DWORD | OT_QWORD)) {
		if (op->operands[1].type & (OT_GPREG | OT_BYTE)) {
			data[l++] = 0xd3;
		} else if (immediate == 1) {
			data[l++] = 0xd1;
		} else {
			data[l++] = 0xc1;
		}
	} else if (op->operands[0].type & OT_BYTE) {
		const Operand *o = &op->operands[0];
		if (o->regs[0] != -1 && o->regs[1] != -1) {
			data[l++] = 0xc0;
			data[l++] = 0x44;
			data[l++] = o->regs[0] | (o->regs[1] << 3);
			data[l++] = (ut8)((o->offset * o->offset_sign) & 0xff);
			data[l++] = immediate;
			return l;
		} else if (op->operands[1].type & (OT_GPREG | OT_WORD)) {
			data[l++] = 0xd2;
		} else if (immediate == 1) {
			data[l++] = 0xd0;
		} else {
			data[l++] = 0xc0;
		}
	}
	if (op->operands[0].type & OT_MEMORY) {
		reg0 = op->operands[0].regs[0];
		mod_byte = 0;
	} else {
		reg0 = op->operands[0].reg;
		mod_byte = 3;
	}
	data[l++] = mod_byte << 6 | modrm << 3 | reg0;
	if (immediate != 1 && !(op->operands[1].type & OT_GPREG)) {
		data[l++] = immediate;
	}
	return l;
}

static int process_1byte_op(RzAsm *a, ut8 *data, const Opcode *op, int op1) {
	is_valid_registers(op);
	int l = 0;
	int mod_byte = 0;
	int reg = 0;
	int rm = 0;
	int rex = 0;
	int mem_ref = 0;
	st32 offset = 0;
	int ebp_reg = 0;

	if (!op->operands[1].is_good_flag) {
		return -1;
	}

	if (op->operands[0].reg == X86R_AL && op->operands[1].type & OT_CONSTANT) {
		data[l++] = op1 + 4;
		data[l++] = op->operands[1].immediate * op->operands[1].sign;
		return l;
	}

	if (a->bits == 64) {
		if (!(op->operands[0].type & op->operands[1].type)) {
			return -1;
		}
	}

	if (a->bits == 64 && ((op->operands[0].type & OT_QWORD) | (op->operands[1].type & OT_QWORD))) {
		if (op->operands[0].extended) {
			rex = 1;
		}
		if (op->operands[1].extended) {
			rex += 4;
		}
		data[l++] = 0x48 | rex;
	}

	if (op->operands[0].type & OT_MEMORY && op->operands[1].type & OT_REGALL) {
		if (a->bits == 64 && (op->operands[0].type & OT_DWORD) &&
			(op->operands[1].type & OT_DWORD)) {
			data[l++] = 0x67;
		}
		if (op->operands[0].type & OT_BYTE && op->operands[1].type & OT_BYTE) {
			data[l++] = op1;
		} else if (op->operands[0].type & (OT_DWORD | OT_QWORD) &&
			op->operands[1].type & (OT_DWORD | OT_QWORD)) {
			data[l++] = op1 + 0x1;
		} else {
			RZ_LOG_ERROR("assembler: x86.nz: %s: mismatched operand sizes\n", op->mnemonic);
			return -1;
		}
		reg = op->operands[1].reg;
		rm = op->operands[0].regs[0];
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (rm == -1) {
			rm = 5;
			mem_ref = 1;
		} else {
			if (offset) {
				mod_byte = 1;
				if (offset < ST8_MIN || offset > ST8_MAX) {
					mod_byte = 2;
				}
			} else if (op->operands[0].regs[1] != X86R_UNDEFINED) {
				rm = 4;
				offset = op->operands[0].regs[1] << 3;
			}
		}
	} else if (op->operands[0].type & OT_REGALL) {
		if (op->operands[1].type & OT_MEMORY) {
			if (op->operands[0].type & OT_BYTE && op->operands[1].type & OT_BYTE) {
				data[l++] = op1 + 0x2;
			} else if (op->operands[0].type & (OT_DWORD | OT_QWORD) &&
				op->operands[1].type & (OT_DWORD | OT_QWORD)) {
				data[l++] = op1 + 0x3;
			} else {
				RZ_LOG_ERROR("assembler: x86.nz: %s: mismatched operand sizes\n", op->mnemonic);
				return -1;
			}
			reg = op->operands[0].reg;
			rm = op->operands[1].regs[0];

			if (op->operands[1].scale[0] > 1) {
				if (op->operands[1].regs[1] != X86R_UNDEFINED) {
					data[l++] = op->operands[0].reg << 3 | 4;
					data[l++] = getsib(op->operands[1].scale[0]) << 6 |
						op->operands[1].regs[0] << 3 |
						op->operands[1].regs[1];
					return l;
				}
				data[l++] = op->operands[0].reg << 3 | 4; // 4 = SIB
				data[l++] = getsib(op->operands[1].scale[0]) << 6 | op->operands[1].regs[0] << 3 | 5;
				data[l++] = op->operands[1].offset * op->operands[1].offset_sign;
				data[l++] = 0;
				data[l++] = 0;
				data[l++] = 0;
				return l;
			}
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			if (offset) {
				mod_byte = 1;
				if (offset < ST8_MIN || offset > ST8_MAX) {
					mod_byte = 2;
				}
			}

		} else if (op->operands[1].type & OT_REGALL) {
			if (op->operands[0].type & OT_BYTE && op->operands[1].type & OT_BYTE) {
				data[l++] = op1;
			} else if (op->operands[0].type & OT_DWORD && op->operands[1].type & OT_DWORD) {
				data[l++] = op1 + 0x1;
			}
			if (a->bits == 64) {
				if (op->operands[0].type & OT_QWORD &&
					op->operands[1].type & OT_QWORD) {
					data[l++] = op1 + 0x1;
				}
			}

			mod_byte = 3;
			reg = op->operands[1].reg;
			rm = op->operands[0].reg;
		}
	}
	if (op->operands[0].regs[0] == X86R_EBP ||
		op->operands[1].regs[0] == X86R_EBP) {
		// reg += 8;
		ebp_reg = 1;
	}
	data[l++] = mod_byte << 6 | reg << 3 | rm;

	if (op->operands[0].regs[0] == X86R_ESP ||
		op->operands[1].regs[0] == X86R_ESP) {
		data[l++] = 0x24;
	}
	if (offset || mem_ref || ebp_reg) {
		// if ((mod_byte > 0 && mod_byte < 3) || mem_ref) {
		data[l++] = offset;
		if (mod_byte == 2 || mem_ref) {
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		}
	}
	return l;
}

static int opadc(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x10);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x10);
}

static int opadd(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x00);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x00);
}

static int opand(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x20);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x20);
}

static int opcmp(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x38);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x38);
}

static int opsub(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x28);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x28);
}

static int opor(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x08);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x08);
}

static int opxadd(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int i = 0;
	if (op->operands_count < 2) {
		return -1;
	}
	if (a->bits == 64) {
		data[i++] = 0x48;
	};
	data[i++] = 0x0f;
	if (op->operands[0].type & OT_BYTE &&
		op->operands[1].type & OT_BYTE) {
		data[i++] = 0xc0;
	} else {
		data[i++] = 0xc1;
	}
	if (op->operands[0].type & OT_REGALL &&
		op->operands[1].type & OT_REGALL) { // TODO memory modes
		data[i] |= 0xc0;
		data[i] |= (op->operands[1].reg << 3);
		data[i++] |= op->operands[0].reg;
	}
	return i;
}

static int opxor(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	if (op->operands_count < 2) {
		return -1;
	}
	if (op->operands[0].type == 0x80 && op->operands[0].reg == X86R_UNDEFINED) {
		return -1;
	}
	if (op->operands[1].type == 0x80 && op->operands[0].reg == X86R_UNDEFINED) {
		return -1;
	}
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x30);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x30);
}

static int opneg(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;

	if (op->operands[0].type & OT_GPREG) {
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		} else if (op->operands[0].type & OT_QWORD) {
			data[l++] = 0x48;
		}

		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		data[l++] = 0xd8 | op->operands[0].reg;
		return l;
	}
	return -1;
}

static int endbr64(RzAsm *a, ut8 *data, const Opcode *op) {
	memcpy(data, "\xf3\x0f\x1e\xfa", 4);
	return 4;
}

static int endbr32(RzAsm *a, ut8 *data, const Opcode *op) {
	memcpy(data, "\xf3\x0f\x1e\xfb", 4);
	return 4;
}

static int opnot(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;

	if (op->operands[0].reg == X86R_UNDEFINED) {
		return -1;
	}

	int size = op->operands[0].type & ALL_SIZE;
	if (op->operands[0].explicit_size) {
		size = op->operands[0].dest_size;
	}
	// rex prefix
	int rex = 1 << 6;
	bool use_rex = false;
	if (size & OT_QWORD) { // W field
		use_rex = true;
		rex |= 1 << 3;
	}
	if (op->operands[0].extended) { // B field
		use_rex = true;
		rex |= 1;
	}

	if (use_rex) {
		data[l++] = rex;
	}
	data[l++] = 0xf7;
	data[l++] = 0xd0 | op->operands[0].reg;

	return l;
}

static int opsbb(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD) {
			return process_16bit_group_1(a, data, op, 0x18);
		}
		if (!is_al_reg(&op->operands[0])) {
			return process_group_1(a, data, op);
		}
	}
	return process_1byte_op(a, data, op, 0x18);
}

static int opbs(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	if (a->bits >= 32 && op->operands[1].type & OT_MEMORY && op->operands[1].reg_size & OT_WORD) {
		return -1;
	}
	if (!(op->operands[1].type & OT_MEMORY) &&
		!((op->operands[0].type & ALL_SIZE) == (op->operands[1].type & ALL_SIZE))) {
		return -1;
	}
	if (op->operands[0].type & OT_GPREG && !(op->operands[0].type & OT_MEMORY)) {
		if (a->bits == 64) {
			if (op->operands[1].type & OT_MEMORY &&
				op->operands[1].reg_size & OT_DWORD) {
				data[l++] = 0x67;
			}
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0x66;
			}
			if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0x48;
			}
		} else if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		}
		data[l++] = 0x0f;
		if (!strcmp(op->mnemonic, "bsf")) {
			data[l++] = 0xbc;
		} else {
			data[l++] = 0xbd;
		}
		if (op->operands[1].type & OT_GPREG && !(op->operands[1].type & OT_MEMORY)) {
			data[l] = 0xc0;
		} else if (!(op->operands[1].type & OT_MEMORY)) {
			return -1;
		}
		data[l] += op->operands[0].reg << 3;
		data[l++] += op->operands[1].reg;
	}
	return l;
}

static int opbswap(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	if (op->operands[0].type & OT_REGALL) {
		is_valid_registers(op);
		if (op->operands[0].reg == X86R_UNDEFINED) {
			return -1;
		}

		if (op->operands[0].type & OT_QWORD) {
			if (op->operands[0].extended) {
				data[l++] = 0x49;
			} else {
				data[l++] = 0x48;
			}
			data[l++] = 0x0f;
			data[l++] = 0xc8 + op->operands[0].reg;
		} else if (op->operands[0].type & OT_DWORD) {
			if (op->operands[0].extended) {
				data[l++] = 0x41;
			}
			data[l++] = 0x0f;
			data[l++] = 0xc8 + op->operands[0].reg;
		} else {
			return -1;
		}
	}
	return l;
}

static int opcall(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int immediate = 0;
	int offset = 0;
	int mod = 0;

	if (op->operands[0].type & OT_GPREG) {
		if (op->operands[0].reg == X86R_UNDEFINED) {
			return -1;
		}
		if (a->bits == 64 && op->operands[0].extended) {
			data[l++] = 0x41;
		}
		data[l++] = 0xff;
		mod = 3;
		data[l++] = mod << 6 | 2 << 3 | op->operands[0].reg;
	} else if (op->operands[0].type & OT_MEMORY) {
		if (op->operands[0].regs[0] == X86R_UNDEFINED) {
			return -1;
		}
		data[l++] = 0xff;
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (offset) {
			mod = 1;
			if (offset > 127 || offset < -128) {
				mod = 2;
			}
		}
		data[l++] = mod << 6 | 2 << 3 | op->operands[0].regs[0];
		if (mod) {
			data[l++] = offset;
			if (mod == 2) {
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
		}
	} else {
		ut64 instr_offset = a->pc;
		data[l++] = 0xe8;
		immediate = op->operands[0].immediate * op->operands[0].sign;
		immediate -= instr_offset + 5;
		data[l++] = immediate;
		data[l++] = immediate >> 8;
		data[l++] = immediate >> 16;
		data[l++] = immediate >> 24;
	}
	return l;
}

static int opcmov(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int mod_byte = 0;
	int offset = 0;

	if (op->operands[0].type & OT_MEMORY ||
		op->operands[1].type & OT_CONSTANT) {
		return -1;
	}

	data[l++] = 0x0f;
	char *cmov = op->mnemonic + 4;
	if (!strcmp(cmov, "o")) {
		data[l++] = 0x40;
	} else if (!strcmp(cmov, "no")) {
		data[l++] = 0x41;
	} else if (!strcmp(cmov, "b") ||
		!strcmp(cmov, "c") ||
		!strcmp(cmov, "nae")) {
		data[l++] = 0x42;
	} else if (!strcmp(cmov, "ae") ||
		!strcmp(cmov, "nb") ||
		!strcmp(cmov, "nc")) {
		data[l++] = 0x43;
	} else if (!strcmp(cmov, "e") ||
		!strcmp(cmov, "z")) {
		data[l++] = 0x44;
	} else if (!strcmp(cmov, "ne") ||
		!strcmp(cmov, "nz")) {
		data[l++] = 0x45;
	} else if (!strcmp(cmov, "be") ||
		!strcmp(cmov, "na")) {
		data[l++] = 0x46;
	} else if (!strcmp(cmov, "a") ||
		!strcmp(cmov, "nbe")) {
		data[l++] = 0x47;
	} else if (!strcmp(cmov, "s")) {
		data[l++] = 0x48;
	} else if (!strcmp(cmov, "ns")) {
		data[l++] = 0x49;
	} else if (!strcmp(cmov, "p") ||
		!strcmp(cmov, "pe")) {
		data[l++] = 0x4a;
	} else if (!strcmp(cmov, "np") ||
		!strcmp(cmov, "po")) {
		data[l++] = 0x4b;
	} else if (!strcmp(cmov, "l") ||
		!strcmp(cmov, "nge")) {
		data[l++] = 0x4c;
	} else if (!strcmp(cmov, "ge") ||
		!strcmp(cmov, "nl")) {
		data[l++] = 0x4d;
	} else if (!strcmp(cmov, "le") ||
		!strcmp(cmov, "ng")) {
		data[l++] = 0x4e;
	} else if (!strcmp(cmov, "g") ||
		!strcmp(cmov, "nle")) {
		data[l++] = 0x4f;
	}

	if (op->operands[0].type & OT_REGALL) {
		if (op->operands[1].type & OT_MEMORY) {
			if (op->operands[1].scale[0] > 1) {
				if (op->operands[1].regs[1] != X86R_UNDEFINED) {
					data[l++] = op->operands[0].reg << 3 | 4;
					data[l++] = getsib(op->operands[1].scale[0]) << 6 |
						op->operands[1].regs[0] << 3 |
						op->operands[1].regs[1];
					return l;
				}
				offset = op->operands[1].offset * op->operands[1].offset_sign;

				if (op->operands[1].scale[0] == 2 && offset) {
					data[l++] = 0x40 | op->operands[0].reg << 3 | 4; // 4 = SIB
				} else {
					data[l++] = op->operands[0].reg << 3 | 4; // 4 = SIB
				}

				if (op->operands[1].scale[0] == 2) {
					data[l++] = op->operands[1].regs[0] << 3 | op->operands[1].regs[0];

				} else {
					data[l++] = getsib(op->operands[1].scale[0]) << 6 |
						op->operands[1].regs[0] << 3 | 5;
				}

				if (offset) {
					data[l++] = offset;
					if (offset < ST8_MIN || offset > ST8_MAX) {
						data[l++] = offset >> 8;
						data[l++] = offset >> 16;
						data[l++] = offset >> 24;
					}
				}
				return l;
			}
			if (op->operands[1].regs[1] != X86R_UNDEFINED) {
				data[l++] = op->operands[0].reg << 3 | 4;
				data[l++] = op->operands[1].regs[1] << 3 | op->operands[1].regs[0];
				return l;
			}

			offset = op->operands[1].offset * op->operands[1].offset_sign;
			if (op->operands[1].offset || op->operands[1].regs[0] == X86R_EBP) {
				mod_byte = 1;
			}
			if (offset < ST8_MIN || offset > ST8_MAX) {
				mod_byte = 2;
			}

			data[l++] = mod_byte << 6 | op->operands[0].reg << 3 | op->operands[1].regs[0];

			if (mod_byte) {
				data[l++] = offset;
				if (mod_byte == 2) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			}
		} else {
			data[l++] = 0xc0 | op->operands[0].reg << 3 | op->operands[1].reg;
		}
	}

	return l;
}

static int opmovx(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int word = 0;
	char *movx = op->mnemonic + 3;

	if (!(op->operands[0].type & OT_REGTYPE && op->operands[1].type & OT_MEMORY)) {
		return -1;
	}
	if (op->operands[1].type & OT_WORD) {
		word = 1;
	}

	data[l++] = 0x0f;
	if (!strcmp(movx, "zx")) {
		data[l++] = 0xb6 + word;
	} else if (!strcmp(movx, "sx")) {
		data[l++] = 0xbe + word;
	}
	data[l++] = op->operands[0].reg << 3 | op->operands[1].regs[0];
	if (op->operands[1].regs[0] == X86R_ESP) {
		data[l++] = 0x24;
	}

	return l;
}

static int opaam(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int immediate = op->operands[0].immediate * op->operands[0].sign;
	data[l++] = 0xd4;
	if (immediate == 0) {
		data[l++] = 0x0a;
	} else if (immediate < 256 && immediate > -129) {
		data[l++] = immediate;
	}
	return l;
}

static int opaad(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int immediate = op->operands[0].immediate * op->operands[0].sign;
	data[l++] = 0xd5;
	if (immediate == 0) {
		data[l++] = 0x0a;
	} else if (immediate < 256 && immediate > -129) {
		data[l++] = immediate;
	}

	return l;
}

static int opdec(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type) {
		RZ_LOG_ERROR("assembler: x86.nz: %s: invalid operands\n", op->mnemonic);
		return -1;
	}
	is_valid_registers(op);
	int l = 0;
	int size = op->operands[0].type & ALL_SIZE;
	if (op->operands[0].explicit_size) {
		size = op->operands[0].dest_size;
	}

	if (size & OT_WORD) {
		data[l++] = 0x66;
	}

	// rex prefix
	int rex = 1 << 6;
	bool use_rex = false;
	if (size & OT_QWORD) { // W field
		use_rex = true;
		rex |= 1 << 3;
	}
	if (op->operands[0].extended) { // B field
		use_rex = true;
		rex |= 1;
	}

	// opcode selection
	int opcode;
	if (size & OT_BYTE) {
		opcode = 0xfe;
	} else {
		opcode = 0xff;
	}

	if (!(op->operands[0].type & OT_MEMORY)) {
		if (use_rex) {
			data[l++] = rex;
		}
		if (a->bits > 32 || size & OT_BYTE) {
			data[l++] = opcode;
		}
		if (a->bits == 32 && size & (OT_DWORD | OT_WORD)) {
			data[l++] = 0x48 | op->operands[0].reg;
		} else {
			data[l++] = 0xc8 | op->operands[0].reg;
		}
		return l;
	}

	// modrm and SIB selection
	bool rip_rel = op->operands[0].regs[0] == X86R_RIP;
	int offset = op->operands[0].offset * op->operands[0].offset_sign;
	int modrm = 0;
	int mod;
	int reg = 0;
	int rm;
	bool use_sib = false;
	int sib = 0;
	// mod
	if (offset == 0) {
		mod = 0;
	} else if (offset < 128 && offset > -129) {
		mod = 1;
	} else {
		mod = 2;
	}

	if (op->operands[0].regs[0] & OT_WORD) {
		if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_SI) {
			rm = B0000;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_DI) {
			rm = B0001;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_SI) {
			rm = B0010;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_DI) {
			rm = B0011;
		} else if (op->operands[0].regs[0] == X86R_SI && op->operands[0].regs[1] == -1) {
			rm = B0100;
		} else if (op->operands[0].regs[0] == X86R_DI && op->operands[0].regs[1] == -1) {
			rm = B0101;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == -1) {
			rm = B0111;
		} else {
			// TODO allow for displacement only when parser is reworked
			return -1;
		}
		modrm = (mod << 6) | (reg << 3) | rm;
	} else {
		// rm
		if (op->operands[0].extended) {
			rm = op->operands[0].reg;
		} else {
			rm = op->operands[0].regs[0];
		}
		//[epb] alone is illegal, so we need to fake a [ebp+0]
		if (rm == 5 && mod == 0) {
			mod = 1;
		}

		// sib
		int index = op->operands[0].regs[1];
		int scale = getsib(op->operands[0].scale[1]);
		if (index != -1) {
			use_sib = true;
			sib = (scale << 6) | (index << 3) | rm;
		} else if (rm == 4) {
			use_sib = true;
			sib = 0x24;
		}
		if (use_sib) {
			rm = B0100;
		}
		if (rip_rel) {
			modrm = (B0000 << 6) | (reg << 3) | B0101;
			sib = (scale << 6) | (B0100 << 3) | B0101;
		} else {
			modrm = (mod << 6) | (reg << 3) | rm;
		}
		modrm |= 1 << 3;
	}

	if (use_rex) {
		data[l++] = rex;
	}
	data[l++] = opcode;
	data[l++] = modrm;
	if (use_sib) {
		data[l++] = sib;
	}
	// offset
	if (mod == 1) {
		data[l++] = offset;
	} else if (op->operands[0].regs[0] & OT_WORD && mod == 2) {
		data[l++] = offset;
		data[l++] = offset >> 8;
	} else if (mod == 2 || rip_rel) {
		data[l++] = offset;
		data[l++] = offset >> 8;
		data[l++] = offset >> 16;
		data[l++] = offset >> 24;
	}

	return l;
}

static int opidiv(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;

	if (op->operands[0].type & OT_QWORD) {
		data[l++] = 0x48;
	}
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		}
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xf8 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opdiv(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;

	if (op->operands[0].type & OT_QWORD) {
		data[l++] = 0x48;
	}
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		}
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xf0 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opimul(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int offset = 0;
	st64 immediate = 0;

	if (op->operands[0].type & OT_QWORD) {
		data[l++] = 0x48;
	}
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		}
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x28 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xe8 | op->operands[0].reg;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_GPREG) {
			if (op->operands[1].type & OT_CONSTANT) {
				if (op->operands[1].immediate == -1) {
					RZ_LOG_ERROR("assembler: x86.nz: %s: immediate operand exceeds max value (imm == -1)\n", op->mnemonic);
					return -1;
				}
				immediate = op->operands[1].immediate * op->operands[1].sign;
				if (op->operands[0].type & OT_GPREG) {
					if (immediate >= 128) {
						data[l++] = 0x69;
					} else {
						data[l++] = 0x6b;
					}
					data[l++] = 0xc0 | op->operands[0].reg << 3 | op->operands[0].reg;
					data[l++] = immediate;
					if (immediate >= 128) {
						data[l++] = immediate >> 8;
						data[l++] = immediate >> 16;
						data[l++] = immediate >> 24;
					}
					if (a->bits == 64 && immediate > UT32_MAX) {
						data[l++] = immediate >> 32;
						data[l++] = immediate >> 40;
						data[l++] = immediate >> 48;
						data[l++] = immediate >> 56;
					}
				}
			} else if (op->operands[1].type & OT_MEMORY) {
				data[l++] = 0x0f;
				data[l++] = 0xaf;
				if (op->operands[1].regs[0] != X86R_UNDEFINED) {
					offset = op->operands[1].offset * op->operands[1].offset_sign;
					if (offset != 0) {
						if (offset >= 128 || offset <= -128) {
							data[l] = 0x80;
						} else {
							data[l] = 0x40;
						}
						data[l++] |= op->operands[0].reg << 3 | op->operands[1].regs[0];
						data[l++] = offset;
						if (offset >= 128 || offset <= -128) {
							data[l++] = offset >> 8;
							data[l++] = offset >> 16;
							data[l++] = offset >> 24;
						}
					} else {
						if (op->operands[1].regs[1] != X86R_UNDEFINED) {
							data[l++] = 0x04 | op->operands[0].reg << 3;
							data[l++] = op->operands[1].regs[1] << 3 | op->operands[1].regs[0];
						} else {
							data[l++] = op->operands[0].reg << 3 | op->operands[1].regs[0];
						}
					}
				} else {
					immediate = op->operands[1].immediate * op->operands[1].sign;
					data[l++] = op->operands[0].reg << 3 | 0x5;
					data[l++] = immediate;
					data[l++] = immediate >> 8;
					data[l++] = immediate >> 16;
					data[l++] = immediate >> 24;
				}
			} else if (op->operands[1].type & OT_GPREG) {
				data[l++] = 0x0f;
				data[l++] = 0xaf;
				data[l++] = 0xc0 | op->operands[0].reg << 3 | op->operands[1].reg;
			}
		}
		break;
	case 3:
		if (op->operands[0].type & OT_GPREG &&
			(op->operands[1].type & OT_GPREG || op->operands[1].type & OT_MEMORY) &&
			op->operands[2].type & OT_CONSTANT) {
			data[l++] = 0x6b;
			if (op->operands[1].type & OT_MEMORY) {
				if (op->operands[1].regs[1] != X86R_UNDEFINED) {
					data[l++] = 0x04 | op->operands[0].reg << 3;
					data[l++] = op->operands[1].regs[0] | op->operands[1].regs[1] << 3;
				} else {
					offset = op->operands[1].offset * op->operands[1].offset_sign;
					if (offset != 0) {
						if (offset >= 128 || offset <= -128) {
							data[l] = 0x80;
						} else {
							data[l] = 0x40;
						}
						data[l++] |= op->operands[0].reg << 3;
						data[l++] = offset;
						if (offset >= 128 || offset <= -128) {
							data[l++] = offset >> 8;
							data[l++] = offset >> 16;
							data[l++] = offset >> 24;
						}
					} else {
						data[l++] = 0x00 | op->operands[0].reg << 3 | op->operands[1].regs[0];
					}
				}
			} else {
				data[l++] = 0xc0 | op->operands[0].reg << 3 | op->operands[1].reg;
			}
			immediate = op->operands[2].immediate * op->operands[2].sign;
			data[l++] = immediate;
			if (immediate >= 128 || immediate <= -128) {
				data[l++] = immediate >> 8;
				data[l++] = immediate >> 16;
				data[l++] = immediate >> 24;
			}
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opin(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	st32 immediate = 0;
	if (op->operands[1].reg == X86R_DX) {
		if (op->operands[0].reg == X86R_AL &&
			op->operands[0].type & OT_BYTE) {
			data[l++] = 0xec;
			return l;
		}
		if (op->operands[0].reg == X86R_AX &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
			data[l++] = 0xed;
			return l;
		}
		if (op->operands[0].reg == X86R_EAX &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0xed;
			return l;
		}
	} else if (op->operands[1].type & OT_CONSTANT) {
		immediate = op->operands[1].immediate * op->operands[1].sign;
		if (immediate > 255 || immediate < -128) {
			return -1;
		}
		if (op->operands[0].reg == X86R_AL &&
			op->operands[0].type & OT_BYTE) {
			data[l++] = 0xe4;
		} else if (op->operands[0].reg == X86R_AX &&
			op->operands[0].type & OT_BYTE) {
			data[l++] = 0x66;
			data[l++] = 0xe5;
		} else if (op->operands[0].reg == X86R_EAX &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0xe5;
		}
		data[l++] = immediate;
	}
	return l;
}

static int opclflush(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int offset = 0;
	int mod_byte = 0;

	if (op->operands[0].type & OT_MEMORY) {
		data[l++] = 0x0f;
		data[l++] = 0xae;
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (offset) {
			if (offset < ST8_MIN || offset > ST8_MAX) {
				mod_byte = 2;
			} else {
				mod_byte = 1;
			}
		}
		data[l++] = (mod_byte << 6) | (7 << 3) | op->operands[0].regs[0];
		if (mod_byte) {
			data[l++] = offset;
			if (mod_byte == 2) {
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
		}
	}
	return l;
}

static int opinc(RzAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type) {
		RZ_LOG_ERROR("assembler: x86.nz: %s: invalid operands\n", op->mnemonic);
		return -1;
	}
	is_valid_registers(op);
	int l = 0;
	int size = op->operands[0].type & ALL_SIZE;
	if (op->operands[0].explicit_size) {
		size = op->operands[0].dest_size;
	}

	if (size & OT_WORD) {
		data[l++] = 0x66;
	}

	// rex prefix
	int rex = 1 << 6;
	bool use_rex = false;
	if (size & OT_QWORD) { // W field
		use_rex = true;
		rex |= 1 << 3;
	}
	if (op->operands[0].extended) { // B field
		use_rex = true;
		rex |= 1;
	}

	// opcode selection
	int opcode;
	if (size & OT_BYTE) {
		opcode = 0xfe;
	} else {
		opcode = 0xff;
	}

	if (!(op->operands[0].type & OT_MEMORY)) {
		if (use_rex) {
			data[l++] = rex;
		}
		if (a->bits > 32 || size & OT_BYTE) {
			data[l++] = opcode;
		}
		if (a->bits == 32 && size & (OT_DWORD | OT_WORD)) {
			data[l++] = 0x40 | op->operands[0].reg;
		} else {
			data[l++] = 0xc0 | op->operands[0].reg;
		}
		return l;
	}

	// modrm and SIB selection
	bool rip_rel = op->operands[0].regs[0] == X86R_RIP;
	int offset = op->operands[0].offset * op->operands[0].offset_sign;
	int modrm = 0;
	int mod;
	int reg = 0;
	int rm;
	bool use_sib = false;
	int sib = 0;
	// mod
	if (offset == 0) {
		mod = 0;
	} else if (offset < 128 && offset > -129) {
		mod = 1;
	} else {
		mod = 2;
	}

	if (op->operands[0].regs[0] & OT_WORD) {
		if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_SI) {
			rm = B0000;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_DI) {
			rm = B0001;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_SI) {
			rm = B0010;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_DI) {
			rm = B0011;
		} else if (op->operands[0].regs[0] == X86R_SI && op->operands[0].regs[1] == -1) {
			rm = B0100;
		} else if (op->operands[0].regs[0] == X86R_DI && op->operands[0].regs[1] == -1) {
			rm = B0101;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == -1) {
			rm = B0111;
		} else {
			// TODO allow for displacement only when parser is reworked
			return -1;
		}
		modrm = (mod << 6) | (reg << 3) | rm;
	} else {
		// rm
		if (op->operands[0].extended) {
			rm = op->operands[0].reg;
		} else {
			rm = op->operands[0].regs[0];
		}
		//[epb] alone is illegal, so we need to fake a [ebp+0]
		if (rm == 5 && mod == 0) {
			mod = 1;
		}

		// sib
		int index = op->operands[0].regs[1];
		int scale = getsib(op->operands[0].scale[1]);
		if (index != -1) {
			use_sib = true;
			sib = (scale << 6) | (index << 3) | rm;
		} else if (rm == 4) {
			use_sib = true;
			sib = 0x24;
		}
		if (use_sib) {
			rm = B0100;
		}
		if (rip_rel) {
			modrm = (B0000 << 6) | (reg << 3) | B0101;
			sib = (scale << 6) | (B0100 << 3) | B0101;
		} else {
			modrm = (mod << 6) | (reg << 3) | rm;
		}
	}

	if (use_rex) {
		data[l++] = rex;
	}
	data[l++] = opcode;
	data[l++] = modrm;
	if (use_sib) {
		data[l++] = sib;
	}
	// offset
	if (mod == 1) {
		data[l++] = offset;
	} else if (op->operands[0].regs[0] & OT_WORD && mod == 2) {
		data[l++] = offset;
		data[l++] = offset >> 8;
	} else if (mod == 2 || rip_rel) {
		data[l++] = offset;
		data[l++] = offset >> 8;
		data[l++] = offset >> 16;
		data[l++] = offset >> 24;
	}

	return l;
}

static int opint(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	if (op->operands[0].type & OT_CONSTANT) {
		st32 immediate = op->operands[0].immediate * op->operands[0].sign;
		if (immediate <= 255 && immediate >= -128) {
			data[l++] = 0xcd;
			data[l++] = immediate;
		}
	}
	return l;
}

static int opjc(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	bool is_short = op->is_short;
	// st64 bigimm = op->operands[0].immediate * op->operands[0].sign;
	st64 immediate = op->operands[0].immediate * op->operands[0].sign;
	if (is_short && (immediate > ST8_MAX || immediate < ST8_MIN)) {
		return l;
	}
	immediate -= a->pc;
	if (immediate > ST32_MAX || immediate < -ST32_MAX) {
		return -1;
	}
	if (!strcmp(op->mnemonic, "jmp")) {
		if (op->operands[0].type & OT_GPREG) {
			data[l++] = 0xff;
			if (op->operands[0].type & OT_MEMORY) {
				if (op->operands[0].offset) {
					int offset = op->operands[0].offset * op->operands[0].offset_sign;
					if (offset >= 128 || offset <= -129) {
						data[l] = 0xa0;
					} else {
						data[l] = 0x60;
					}
					data[l++] |= op->operands[0].regs[0];
					if (op->operands[0].regs[0] == X86R_ESP) {
						data[l++] = 0x24;
					}
					data[l++] = offset;
					if (op->operands[0].offset >= 0x80) {
						data[l++] = offset >> 8;
						data[l++] = offset >> 16;
						data[l++] = offset >> 24;
					}
				} else {
					data[l++] = 0x20 | op->operands[0].regs[0];
				}
			} else {
				data[l++] = 0xe0 | op->operands[0].reg;
			}
		} else {
			if (-0x80 <= (immediate - 2) && (immediate - 2) <= 0x7f) {
				/* relative byte address */
				data[l++] = 0xeb;
				data[l++] = immediate - 2;
			} else {
				/* relative address */
				immediate -= 5;
				data[l++] = 0xe9;
				data[l++] = immediate;
				data[l++] = immediate >> 8;
				data[l++] = immediate >> 16;
				data[l++] = immediate >> 24;
			}
		}
		return l;
	}
	if (immediate <= 0x81 && immediate > -0x7f) {
		is_short = true;
	}
	if (a->bits == 16 && (immediate > 0x81 || immediate < -0x7e)) {
		data[l++] = 0x66;
		is_short = false;
		immediate--;
	}

	if (!is_short) {
		data[l++] = 0x0f;
	}
	if (!strcmp(op->mnemonic, "ja") ||
		!strcmp(op->mnemonic, "jnbe")) {
		data[l++] = 0x87;
	} else if (!strcmp(op->mnemonic, "jae") ||
		!strcmp(op->mnemonic, "jnb") ||
		!strcmp(op->mnemonic, "jnc")) {
		data[l++] = 0x83;
	} else if (!strcmp(op->mnemonic, "jz") ||
		!strcmp(op->mnemonic, "je")) {
		data[l++] = 0x84;
	} else if (!strcmp(op->mnemonic, "jb") ||
		!strcmp(op->mnemonic, "jnae") ||
		!strcmp(op->mnemonic, "jc")) {
		data[l++] = 0x82;
	} else if (!strcmp(op->mnemonic, "jbe") ||
		!strcmp(op->mnemonic, "jna")) {
		data[l++] = 0x86;
	} else if (!strcmp(op->mnemonic, "jg") ||
		!strcmp(op->mnemonic, "jnle")) {
		data[l++] = 0x8f;
	} else if (!strcmp(op->mnemonic, "jge") ||
		!strcmp(op->mnemonic, "jnl")) {
		data[l++] = 0x8d;
	} else if (!strcmp(op->mnemonic, "jl") ||
		!strcmp(op->mnemonic, "jnge")) {
		data[l++] = 0x8c;
	} else if (!strcmp(op->mnemonic, "jle") ||
		!strcmp(op->mnemonic, "jng")) {
		data[l++] = 0x8e;
	} else if (!strcmp(op->mnemonic, "jne") ||
		!strcmp(op->mnemonic, "jnz")) {
		data[l++] = 0x85;
	} else if (!strcmp(op->mnemonic, "jno")) {
		data[l++] = 0x81;
	} else if (!strcmp(op->mnemonic, "jnp") ||
		!strcmp(op->mnemonic, "jpo")) {
		data[l++] = 0x8b;
	} else if (!strcmp(op->mnemonic, "jns")) {
		data[l++] = 0x89;
	} else if (!strcmp(op->mnemonic, "jo")) {
		data[l++] = 0x80;
	} else if (!strcmp(op->mnemonic, "jp") ||
		!strcmp(op->mnemonic, "jpe")) {
		data[l++] = 0x8a;
	} else if (!strcmp(op->mnemonic, "js") ||
		!strcmp(op->mnemonic, "jz")) {
		data[l++] = 0x88;
	}
	if (is_short) {
		data[l - 1] -= 0x10;
	}

	immediate -= is_short ? 2 : 6;
	data[l++] = immediate;
	if (!is_short) {
		data[l++] = immediate >> 8;
		data[l++] = immediate >> 16;
		data[l++] = immediate >> 24;
	}
	return l;
}

static int oplea(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	int mod = 0;
	st32 offset = 0;
	int reg = 0;
	int rm = 0;
	if (op->operands[0].type & OT_REGALL &&
		op->operands[1].type & (OT_MEMORY | OT_CONSTANT)) {
		if (a->bits == 64) {
			data[l++] = 0x48;
		}
		data[l++] = 0x8d;
		if (op->operands[1].regs[0] == X86R_UNDEFINED) {
			// RIP-relative LEA
			ut64 offset = op->operands[1].offset - a->pc;
			if (data[0] == 0x48) {
				offset -= 7;
			}
			ut32 high = 0xff00 & offset;
			data[l++] = op->operands[0].reg << 3 | 5;
			data[l++] = offset;
			data[l++] = high >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
			return l;
		} else {
			reg = op->operands[0].reg;
			rm = op->operands[1].regs[0];

			offset = op->operands[1].offset * op->operands[1].offset_sign;
			if (op->operands[1].regs[0] == X86R_RIP) {
				// RIP-relative LEA (not caught above, so "offset" is already relative)
				data[l++] = reg << 3 | 5;
				data[l++] = offset;
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
				return l;
			}
			if (offset != 0 || op->operands[1].regs[0] == X86R_EBP) {
				mod = 1;
				if (offset >= 128 || offset < -128) {
					mod = 2;
				}
				data[l++] = mod << 6 | reg << 3 | rm;
				if (op->operands[1].regs[0] == X86R_ESP) {
					data[l++] = 0x24;
				}
				data[l++] = offset;
				if (mod == 2) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			} else {
				data[l++] = op->operands[0].reg << 3 | op->operands[1].regs[0];
				if (op->operands[1].regs[0] == X86R_ESP) {
					data[l++] = 0x24;
				}
			}
		}
	}
	return l;
}

static int oples(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	int offset = 0;
	int mod = 0;

	if (op->operands[1].type & OT_MEMORY) {
		data[l++] = 0xc4;
		if (op->operands[1].type & OT_GPREG) {
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			if (offset) {
				mod = 1;
				if (offset > 128 || offset < -128) {
					mod = 2;
				}
			}
			data[l++] = mod << 6 | op->operands[0].reg << 3 | op->operands[1].regs[0];
			if (mod) {
				data[l++] = offset;
				if (mod > 1) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			}
		} else {
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			data[l++] = 0x05;
			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		}
	}
	return l;
}

static int opmov(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	st64 offset = 0;
	int mod = 0;
	int base = 0;
	int rex = 0;
	ut64 immediate = 0;
	if (op->operands[1].type & OT_CONSTANT) {
		if (!op->operands[1].is_good_flag) {
			return -1;
		}
		if (op->operands[1].immediate == -1 && a->num && a->num->nc.errors > 0) {
			return -1;
		}
		immediate = op->operands[1].immediate * op->operands[1].sign;
		if (op->operands[0].type & OT_GPREG && !(op->operands[0].type & OT_MEMORY)) {
			if ((op->operands[0].type & OT_DWORD) &&
				immediate > UT32_MAX && immediate < 0xffffffff80000000ULL /* -0x80000000 */) {
				return -1;
			}
			bool imm32in64 = false;
			if (a->bits == 64 && (op->operands[0].type & OT_QWORD)) {
				if (op->operands[0].extended) {
					data[l++] = 0x49;
				} else {
					data[l++] = 0x48;
				}
			} else if (op->operands[0].extended) {
				data[l++] = 0x41;
			}
			if (op->operands[0].type & OT_WORD) {
				if (a->bits > 16) {
					data[l++] = 0x66;
				}
			}
			if (op->operands[0].type & OT_BYTE) {
				data[l++] = 0xb0 | op->operands[0].reg;
				data[l++] = immediate;
			} else {
				if (a->bits == 64 && (op->operands[0].type & OT_QWORD) &&
					(immediate <= ST32_MAX || immediate >= 0xffffffff80000000ULL /* -0x80000000 */)) {
					data[l++] = 0xc7;
					data[l++] = 0xc0 | op->operands[0].reg;
					imm32in64 = true;
				} else {
					data[l++] = 0xb8 | op->operands[0].reg;
				}
				data[l++] = immediate;
				data[l++] = immediate >> 8;
				if (!(op->operands[0].type & OT_WORD)) {
					data[l++] = immediate >> 16;
					data[l++] = immediate >> 24;
				}
				if (a->bits == 64 &&
					(((op->operands[0].type & OT_QWORD) && !imm32in64) ||
						(immediate > UT32_MAX && immediate < 0xffffffff80000000ULL /* -0x80000000 */))) {
					data[l++] = immediate >> 32;
					data[l++] = immediate >> 40;
					data[l++] = immediate >> 48;
					data[l++] = immediate >> 56;
				}
			}
		} else if (op->operands[0].type & OT_MEMORY) {
			if (!op->operands[0].explicit_size) {
				if (op->operands[0].type & OT_GPREG) {
					((Opcode *)op)->operands[0].dest_size = op->operands[0].reg_size;
				} else {
					return -1;
				}
			}

			int dest_bits = 8 * ((op->operands[0].dest_size & ALL_SIZE) >> OPSIZE_SHIFT);
			int reg_bits = 8 * ((op->operands[0].reg_size & ALL_SIZE) >> OPSIZE_SHIFT);
			int offset = op->operands[0].offset * op->operands[0].offset_sign;

			// addr_size_override prefix
			bool use_aso = false;
			if (reg_bits < a->bits) {
				use_aso = true;
			}

			// op_size_override prefix
			bool use_oso = false;
			if (dest_bits == 16) {
				use_oso = true;
			}

			bool rip_rel = op->operands[0].regs[0] == X86R_RIP;

			// rex prefix
			int rex = 1 << 6;
			bool use_rex = false;
			if (dest_bits == 64) { // W field
				use_rex = true;
				rex |= 1 << 3;
			}
			if (op->operands[0].extended) { // B field
				use_rex = true;
				rex |= 1;
			}

			// opcode selection
			int opcode;
			if (dest_bits == 8) {
				opcode = 0xc6;
			} else {
				opcode = 0xc7;
			}

			// modrm and SIB selection
			int modrm = 0;
			int mod;
			int reg = 0;
			int rm;
			bool use_sib = false;
			int sib;
			// mod
			if (offset == 0) {
				mod = 0;
			} else if (offset < 128 && offset > -129) {
				mod = 1;
			} else {
				mod = 2;
			}

			if (reg_bits == 16) {
				if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_SI) {
					rm = B0000;
				} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_DI) {
					rm = B0001;
				} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_SI) {
					rm = B0010;
				} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_DI) {
					rm = B0011;
				} else if (op->operands[0].regs[0] == X86R_SI && op->operands[0].regs[1] == -1) {
					rm = B0100;
				} else if (op->operands[0].regs[0] == X86R_DI && op->operands[0].regs[1] == -1) {
					rm = B0101;
				} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == -1) {
					rm = B0111;
				} else {
					// TODO allow for displacement only when parser is reworked
					return -1;
				}
				modrm = (mod << 6) | (reg << 3) | rm;
			} else {
				// rm
				if (op->operands[0].extended) {
					rm = op->operands[0].reg;
				} else {
					rm = op->operands[0].regs[0];
				}
				//[epb] alone is illegal, so we need to fake a [ebp+0]
				if (rm == 5 && mod == 0) {
					mod = 1;
				}

				// sib
				int index = op->operands[0].regs[1];
				int scale = getsib(op->operands[0].scale[1]);
				if (index != -1) {
					use_sib = true;
					sib = (scale << 6) | (index << 3) | rm;
				} else if (rm == 4) {
					use_sib = true;
					sib = 0x24;
				}
				if (use_sib) {
					rm = B0100;
				}
				if (rip_rel) {
					modrm = (B0000 << 6) | (reg << 3) | B0101;
					sib = (scale << 6) | (B0100 << 3) | B0101;
				} else {
					modrm = (mod << 6) | (reg << 3) | rm;
				}
			}

			// build the final result
			if (use_aso) {
				data[l++] = 0x67;
			}
			if (use_oso) {
				data[l++] = 0x66;
			}
			if (use_rex) {
				data[l++] = rex;
			}
			data[l++] = opcode;
			data[l++] = modrm;
			if (use_sib) {
				data[l++] = sib;
			}
			// offset
			if (mod == 1) {
				data[l++] = offset;
			} else if (reg_bits == 16 && mod == 2) {
				data[l++] = offset;
				data[l++] = offset >> 8;
			} else if (mod == 2 || rip_rel) {
				data[l++] = offset;
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
			// immediate
			int byte;
			for (byte = 0; byte < dest_bits && byte < 32; byte += 8) {
				data[l++] = (immediate >> byte);
			}
		}
	} else if (op->operands[1].type & OT_REGALL &&
		!(op->operands[1].type & OT_MEMORY)) {
		if (op->operands[0].type & OT_CONSTANT) {
			return -1;
		}
		if (op->operands[0].type & OT_REGTYPE & OT_SEGMENTREG &&
			op->operands[1].type & OT_REGTYPE & OT_SEGMENTREG) {
			return -1;
		}
		if (is_debug_or_control(op->operands[0]) &&
			!(op->operands[1].type & OT_REGTYPE & OT_GPREG)) {
			return -1;
		}
		if (is_debug_or_control(op->operands[1]) &&
			!(op->operands[0].type & OT_REGTYPE & OT_GPREG)) {
			return -1;
		}
		// Check reg sizes match
		if (op->operands[0].type & OT_REGTYPE && op->operands[1].type & OT_REGTYPE) {
			if (!((op->operands[0].type & ALL_SIZE) &
				    (op->operands[1].type & ALL_SIZE))) {
				return -1;
			}
		}

		if (a->bits == 64) {
			if (op->operands[0].extended) {
				rex = 1;
			}
			if (op->operands[1].extended) {
				rex += 4;
			}
			if (op->operands[1].type & OT_QWORD) {
				if (!(op->operands[0].type & OT_QWORD)) {
					data[l++] = 0x67;
					data[l++] = 0x48;
				}
			}
			if (op->operands[1].type & OT_QWORD &&
				op->operands[0].type & OT_QWORD) {
				data[l++] = 0x48 | rex;
			}
			if (op->operands[1].type & OT_DWORD &&
				op->operands[0].type & OT_DWORD) {
				data[l++] = 0x40 | rex;
			}
		} else if (op->operands[0].extended && op->operands[1].extended) {
			data[l++] = 0x45;
		}
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (op->operands[1].type & OT_REGTYPE & OT_SEGMENTREG) {
			data[l++] = 0x8c;
		} else if (is_debug_or_control(op->operands[0])) {
			data[l++] = 0x0f;
			if (op->operands[0].type & OT_REGTYPE & OT_DEBUGREG) {
				data[l++] = 0x23;
			} else {
				data[l++] = 0x22;
			}
		} else if (is_debug_or_control(op->operands[1])) {
			data[l++] = 0x0f;
			if (op->operands[1].type & OT_REGTYPE & OT_DEBUGREG) {
				data[l++] = 0x21;
			} else {
				data[l++] = 0x20;
			}
		} else {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0x66;
			}
			data[l++] = (op->operands[0].type & OT_BYTE) ? 0x88 : 0x89;
		}

		if (op->operands[0].scale[0] > 1) {
			data[l++] = op->operands[1].reg << 3 | 4;
			data[l++] = getsib(op->operands[0].scale[0]) << 6 |
				op->operands[0].regs[0] << 3 | 5;

			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;

			return l;
		}

		if (!(op->operands[0].type & OT_MEMORY)) {
			if (op->operands[0].reg == X86R_UNDEFINED ||
				op->operands[1].reg == X86R_UNDEFINED) {
				return -1;
			}
			mod = 0x3;
			data[l++] = (is_debug_or_control(op->operands[0]))
				? mod << 6 | op->operands[0].reg << 3 | op->operands[1].reg
				: mod << 6 | op->operands[1].reg << 3 | op->operands[0].reg;
		} else if (op->operands[0].regs[0] == X86R_UNDEFINED) {
			data[l++] = op->operands[1].reg << 3 | 0x5;
			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		} else {
			if (op->operands[0].type & OT_MEMORY) {
				if (op->operands[0].regs[1] != X86R_UNDEFINED) {
					data[l++] = op->operands[1].reg << 3 | 0x4;
					data[l++] = op->operands[0].regs[1] << 3 | op->operands[0].regs[0];
					return l;
				}
				if (offset) {
					mod = (offset > 128 || offset < -129) ? 0x2 : 0x1;
				}
				if (op->operands[0].regs[0] == X86R_EBP) {
					mod = 0x2;
				}
				data[l++] = mod << 6 | op->operands[1].reg << 3 | op->operands[0].regs[0];
				if (op->operands[0].regs[0] == X86R_ESP) {
					data[l++] = 0x24;
				}
				if (offset) {
					data[l++] = offset;
				}
				if (mod == 2) {
					// warning C4293: '>>': shift count negative or too big, undefined behavior
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			}
		}
	} else if (op->operands[1].type & OT_MEMORY) {
		if (op->operands[0].type & OT_MEMORY) {
			return -1;
		}
		offset = op->operands[1].offset * op->operands[1].offset_sign;
		if (op->operands[0].reg == X86R_EAX && op->operands[1].regs[0] == X86R_UNDEFINED) {
			if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0x48;
			} else if (op->operands[0].type & OT_WORD && a->bits != 16) {
				data[l++] = 0x66;
			}
			if (op->operands[0].type & OT_BYTE) {
				data[l++] = 0xa0;
			} else {
				data[l++] = 0xa1;
			}
			data[l++] = offset;
			data[l++] = offset >> 8;
			if (a->bits >= 32) {
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
				if (a->bits == 64) {
					data[l++] = offset >> 32;
					data[l++] = offset >> 40;
					data[l++] = offset >> 48;
					data[l++] = offset >> 56;
				}
			}
			return l;
		}
		if (op->operands[0].type & OT_BYTE && a->bits == 64 && op->operands[1].regs[0]) {
			if (op->operands[1].regs[0] >= X86R_R8 &&
				op->operands[0].reg < 4) {
				data[l++] = 0x41;
				data[l++] = 0x8a;
				data[l++] = op->operands[0].reg << 3 | (op->operands[1].regs[0] - 8);
				return l;
			}
			return -1;
		}

		if (op->operands[1].type & OT_REGTYPE & OT_SEGMENTREG) {
			if (op->operands[1].scale[0] == 0) {
				return -1;
			}
			data[l++] = SEG_REG_PREFIXES[op->operands[1].regs[0] % 6];
			data[l++] = 0x8b;
			data[l++] = (((ut32)op->operands[0].reg) << 3) | 0x5;
			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
			return l;
		}

		if (a->bits == 64) {
			if (op->operands[0].type & OT_QWORD) {
				if (!(op->operands[1].type & OT_QWORD)) {
					if (op->operands[1].regs[0] != -1) {
						data[l++] = 0x67;
					}
					data[l++] = 0x48;
				}
			} else if (op->operands[1].type & OT_DWORD) {
				data[l++] = 0x44;
			} else if (!(op->operands[1].type & OT_QWORD)) {
				data[l++] = 0x67;
			}
			if (op->operands[1].type & OT_QWORD &&
				op->operands[0].type & OT_QWORD) {
				data[l++] = 0x48;
			}
		}

		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
			data[l++] = op->operands[1].type & OT_BYTE ? 0x8a : 0x8b;
		} else {
			data[l++] = (op->operands[1].type & OT_BYTE ||
					    op->operands[0].type & OT_BYTE)
				? 0x8a
				: 0x8b;
		}

		if (op->operands[1].regs[0] == X86R_UNDEFINED) {
			if (a->bits == 64) {
				data[l++] = op->operands[0].reg << 3 | 0x4;
				data[l++] = 0x25;
			} else {
				data[l++] = op->operands[0].reg << 3 | 0x5;
			}
			data[l++] = offset;
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		} else {
			if (op->operands[1].scale[0] > 1) {
				data[l++] = op->operands[0].reg << 3 | 4;

				if (op->operands[1].scale[0] >= 2) {
					base = 5;
				}
				if (base) {
					data[l++] = getsib(op->operands[1].scale[0]) << 6 | op->operands[1].regs[0] << 3 | base;
				} else {
					data[l++] = getsib(op->operands[1].scale[0]) << 3 | op->operands[1].regs[0];
				}
				if (offset || base) {
					data[l++] = offset;
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
				return l;
			}
			if (op->operands[1].regs[1] != X86R_UNDEFINED) {
				data[l++] = op->operands[0].reg << 3 | 0x4;
				data[l++] = op->operands[1].regs[1] << 3 | op->operands[1].regs[0];
				return l;
			}

			if (offset || op->operands[1].regs[0] == X86R_EBP) {
				mod = 0x2;
				if (op->operands[1].offset > 127) {
					mod = 0x4;
				}
			}
			if (a->bits == 64 && offset && op->operands[0].type & OT_QWORD) {
				if (op->operands[1].regs[0] == X86R_RIP) {
					data[l++] = 0x5;
				} else {
					const ut8 pfx = (op->operands[1].offset > 127) ? 0x80 : 0x40;
					data[l++] = pfx | op->operands[0].reg << 3 | op->operands[1].regs[0];
				}
				if (op->operands[1].offset > 127) {
					mod = 0x1;
				}
			} else {
				if (op->operands[1].regs[0] == X86R_EIP && (op->operands[0].type & OT_DWORD)) {
					data[l++] = 0x0d;
				} else if (op->operands[1].regs[0] == X86R_RIP && (op->operands[0].type & OT_QWORD)) {
					data[l++] = 0x05;
				} else {
					data[l++] = mod << 5 | op->operands[0].reg << 3 | op->operands[1].regs[0];
				}
			}
			if (op->operands[1].regs[0] == X86R_ESP) {
				data[l++] = 0x24;
			}
			if (mod >= 0x2) {
				data[l++] = offset;
				if (op->operands[1].offset > 128 || op->operands[1].regs[0] == X86R_EIP) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			} else if (a->bits == 64 && (offset || op->operands[1].regs[0] == X86R_RIP)) {
				data[l++] = offset;
				if (op->operands[1].offset > 127 || op->operands[1].regs[0] == X86R_RIP) {
					data[l++] = offset >> 8;
					data[l++] = offset >> 16;
					data[l++] = offset >> 24;
				}
			}
		}
	}
	return l;
}

// Only for MOV r64, imm64
static int opmovabs(RzAsm *a, ut8 *data, const Opcode *op) {
	if (!(a->bits == 64 && (op->operands[0].type & OT_GPREG) && !(op->operands[0].type & OT_MEMORY) &&
		    (op->operands[0].type & OT_QWORD) && (op->operands[1].type & OT_CONSTANT))) {
		return -1;
	}
	int l = 0;
	int byte_shift;
	ut64 immediate;
	if (op->operands[0].extended) {
		data[l++] = 0x49;
	} else {
		data[l++] = 0x48;
	}
	data[l++] = 0xb8 | op->operands[0].reg;
	immediate = op->operands[1].immediate * op->operands[1].sign;
	for (byte_shift = 0; byte_shift < 8; byte_shift++) {
		data[l++] = immediate >> (byte_shift * 8);
	}
	return l;
}

static int opmul(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;

	if (op->operands[0].type & OT_QWORD) {
		data[l++] = 0x48;
	}
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x66;
		}
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x20 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xe0 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int oppop(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int offset = 0;
	int mod = 0;
	if (op->operands[0].type & OT_GPREG) {
		if (op->operands[0].type & OT_MEMORY) {
			return -1;
		}
		if (op->operands[0].type & OT_REGTYPE & OT_SEGMENTREG) {
			ut8 base;
			if (op->operands[0].reg & X86R_FS) {
				data[l++] = 0x0f;
				base = 0x81;
			} else {
				base = 0x7;
			}
			data[l++] = base + (8 * op->operands[0].reg);
		} else {
			if (op->operands[0].extended && a->bits == 64) {
				data[l++] = 0x41;
			}
			ut8 base = 0x58;
			data[l++] = base + op->operands[0].reg;
		}
	} else if (op->operands[0].type & OT_MEMORY) {
		data[l++] = 0x8f;
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (offset != 0 || op->operands[0].regs[0] == X86R_EBP) {
			mod = 1;
			if (offset >= 128 || offset < -128) {
				mod = 2;
			}
			data[l++] = mod << 6 | op->operands[0].regs[0];
			if (op->operands[0].regs[0] == X86R_ESP) {
				data[l++] = 0x24;
			}
			data[l++] = offset;
			if (mod == 2) {
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
		} else {
			data[l++] = op->operands[0].regs[0];
			if (op->operands[0].regs[0] == X86R_ESP) {
				data[l++] = 0x24;
			}
		}
	}
	return l;
}

static int oppush(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int mod = 0;
	st32 immediate = 0;
	st32 offset = 0;
	if (op->operands[0].type & OT_GPREG &&
		!(op->operands[0].type & OT_MEMORY)) {
		if (op->operands[0].type & OT_REGTYPE & OT_SEGMENTREG) {
			ut8 base;
			if (op->operands[0].reg & X86R_FS) {
				data[l++] = 0x0f;
				base = 0x80;
			} else {
				base = 0x6;
			}
			data[l++] = base + (8 * op->operands[0].reg);
		} else {
			if (op->operands[0].extended && a->bits == 64) {
				data[l++] = 0x41;
			}
			ut8 base = 0x50;
			if (op->operands[0].reg == X86R_RIP) {
				RZ_LOG_ERROR("assembler: x86.nz: %s: invalid register (rip)\n", op->mnemonic);
				return -1;
			}
			data[l++] = base + op->operands[0].reg;
		}
	} else if (op->operands[0].type & OT_MEMORY) {
		data[l++] = 0xff;
		offset = op->operands[0].offset * op->operands[0].offset_sign;
		if (offset != 0 || op->operands[0].regs[0] == X86R_EBP) {
			mod = 1;
			if (offset >= 128 || offset < -128) {
				mod = 2;
			}
			data[l++] = mod << 6 | 6 << 3 | op->operands[0].regs[0];
			if (op->operands[0].regs[0] == X86R_ESP) {
				data[l++] = 0x24;
			}
			data[l++] = offset;
			if (mod == 2) {
				data[l++] = offset >> 8;
				data[l++] = offset >> 16;
				data[l++] = offset >> 24;
			}
		} else {
			mod = 3;
			data[l++] = mod << 4 | op->operands[0].regs[0];
			if (op->operands[0].regs[0] == X86R_ESP) {
				data[l++] = 0x24;
			}
		}
	} else {
		immediate = op->operands[0].immediate * op->operands[0].sign;
		if (immediate >= 128 || immediate < -128) {
			data[l++] = 0x68;
			data[l++] = immediate;
			data[l++] = immediate >> 8;
			data[l++] = immediate >> 16;
			data[l++] = immediate >> 24;
		} else {
			data[l++] = 0x6a;
			data[l++] = immediate;
		}
	}
	return l;
}

static int opout(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	st32 immediate = 0;
	if (op->operands[0].reg == X86R_DX) {
		if (op->operands[1].reg == X86R_AL && op->operands[1].type & OT_BYTE) {
			data[l++] = 0xee;
			return l;
		}
		if (op->operands[1].reg == X86R_AX && op->operands[1].type & OT_WORD) {
			data[l++] = 0x66;
			data[l++] = 0xef;
			return l;
		}
		if (op->operands[1].reg == X86R_EAX && op->operands[1].type & OT_DWORD) {
			data[l++] = 0xef;
			return l;
		}
	} else if (op->operands[0].type & OT_CONSTANT) {
		immediate = op->operands[0].immediate * op->operands[0].sign;
		if (immediate > 255 || immediate < -128) {
			return -1;
		}
		if (op->operands[1].reg == X86R_AL && op->operands[1].type & OT_BYTE) {
			data[l++] = 0xe6;
		} else if (op->operands[1].reg == X86R_AX && op->operands[1].type & OT_WORD) {
			data[l++] = 0x66;
			data[l++] = 0xe7;
		} else if (op->operands[1].reg == X86R_EAX && op->operands[1].type & OT_DWORD) {
			data[l++] = 0xe7;
		} else {
			return -1;
		}
		data[l++] = immediate;
	} else {
		return -1;
	}
	return l;
}

static int oploop(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	data[l++] = 0xe2;
	st8 delta = op->operands[0].immediate - a->pc - 2;
	data[l++] = (ut8)delta;
	return l;
}

static int opret(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	int immediate = 0;
	if (a->bits == 16) {
		data[l++] = 0xc3;
		return l;
	}
	if (op->operands[0].type == OT_UNKNOWN) {
		data[l++] = 0xc3;
	} else if (op->operands[0].type & (OT_CONSTANT | OT_WORD)) {
		data[l++] = 0xc2;
		immediate = op->operands[0].immediate * op->operands[0].sign;
		data[l++] = immediate;
		data[l++] = immediate << 8;
	}
	return l;
}

static int opretf(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	st32 immediate = 0;
	if (op->operands[0].type & OT_CONSTANT) {
		immediate = op->operands[0].immediate * op->operands[0].sign;
		data[l++] = 0xca;
		data[l++] = immediate;
		data[l++] = immediate >> 8;
	} else if (op->operands[0].type == OT_UNKNOWN) {
		data[l++] = 0xcb;
	}
	return l;
}

static int opstos(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	if (!strcmp(op->mnemonic, "stosw")) {
		data[l++] = 0x66;
	}
	if (!strcmp(op->mnemonic, "stosb")) {
		data[l++] = 0xaa;
	} else if (!strcmp(op->mnemonic, "stosw")) {
		data[l++] = 0xab;
	} else if (!strcmp(op->mnemonic, "stosd")) {
		data[l++] = 0xab;
	}
	return l;
}

static int opset(RzAsm *a, ut8 *data, const Opcode *op) {
	if (!(op->operands[0].type & (OT_GPREG | OT_BYTE))) {
		return -1;
	}
	int l = 0;
	int mod = 0;
	int reg = op->operands[0].regs[0];

	data[l++] = 0x0f;
	if (!strcmp(op->mnemonic, "seto")) {
		data[l++] = 0x90;
	} else if (!strcmp(op->mnemonic, "setno")) {
		data[l++] = 0x91;
	} else if (!strcmp(op->mnemonic, "setb") ||
		!strcmp(op->mnemonic, "setnae") ||
		!strcmp(op->mnemonic, "setc")) {
		data[l++] = 0x92;
	} else if (!strcmp(op->mnemonic, "setnb") ||
		!strcmp(op->mnemonic, "setae") ||
		!strcmp(op->mnemonic, "setnc")) {
		data[l++] = 0x93;
	} else if (!strcmp(op->mnemonic, "setz") ||
		!strcmp(op->mnemonic, "sete")) {
		data[l++] = 0x94;
	} else if (!strcmp(op->mnemonic, "setnz") ||
		!strcmp(op->mnemonic, "setne")) {
		data[l++] = 0x95;
	} else if (!strcmp(op->mnemonic, "setbe") ||
		!strcmp(op->mnemonic, "setna")) {
		data[l++] = 0x96;
	} else if (!strcmp(op->mnemonic, "setnbe") ||
		!strcmp(op->mnemonic, "seta")) {
		data[l++] = 0x97;
	} else if (!strcmp(op->mnemonic, "sets")) {
		data[l++] = 0x98;
	} else if (!strcmp(op->mnemonic, "setns")) {
		data[l++] = 0x99;
	} else if (!strcmp(op->mnemonic, "setp") ||
		!strcmp(op->mnemonic, "setpe")) {
		data[l++] = 0x9a;
	} else if (!strcmp(op->mnemonic, "setnp") ||
		!strcmp(op->mnemonic, "setpo")) {
		data[l++] = 0x9b;
	} else if (!strcmp(op->mnemonic, "setl") ||
		!strcmp(op->mnemonic, "setnge")) {
		data[l++] = 0x9c;
	} else if (!strcmp(op->mnemonic, "setnl") ||
		!strcmp(op->mnemonic, "setge")) {
		data[l++] = 0x9d;
	} else if (!strcmp(op->mnemonic, "setle") ||
		!strcmp(op->mnemonic, "setng")) {
		data[l++] = 0x9e;
	} else if (!strcmp(op->mnemonic, "setnle") ||
		!strcmp(op->mnemonic, "setg")) {
		data[l++] = 0x9f;
	} else {
		return -1;
	}
	if (!(op->operands[0].type & OT_MEMORY)) {
		mod = 3;
		reg = op->operands[0].reg;
	}
	data[l++] = mod << 6 | reg;
	return l;
}

static int optest(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	if (!op->operands[0].type || !op->operands[1].type) {
		RZ_LOG_ERROR("assembler: x86.nz: %s: invalid operands\n", op->mnemonic);
		return -1;
	}
	if (a->bits == 64) {
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].reg_size & OT_DWORD) {
			data[l++] = 0x67;
		}
		if (op->operands[0].type & OT_QWORD) {
			if (op->operands[0].extended &&
				op->operands[1].extended) {
				data[l++] = 0x4d;
			} else {
				data[l++] = 0x48;
			}
		}
	}

	if (op->operands[1].type & OT_CONSTANT) {
		if (op->operands[0].type & OT_BYTE) {
			data[l++] = 0xf6;
		} else {
			if (op->operands[0].type & OT_WORD && a->bits != 16) {
				data[l++] = 0x66;
			}
			data[l++] = 0xf7;
		}
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x00 | op->operands[0].reg;
		} else {
			data[l++] = 0xc0 | op->operands[0].reg;
		}
		data[l++] = op->operands[1].immediate >> 0;
		if (op->operands[0].type & OT_BYTE) {
			return l;
		}
		data[l++] = op->operands[1].immediate >> 8;
		if (op->operands[0].type & OT_WORD) {
			return l;
		}
		data[l++] = op->operands[1].immediate >> 16;
		data[l++] = op->operands[1].immediate >> 24;
		return l;
	}
	if (op->operands[0].type & OT_BYTE ||
		op->operands[1].type & OT_BYTE) {
		data[l++] = 0x84;
	} else {
		data[l++] = 0x85;
	}
	if (op->operands[0].type & OT_MEMORY) {
		data[l++] = 0x00 | op->operands[1].reg << 3 | op->operands[0].regs[0];
	} else {
		if (op->operands[1].type & OT_MEMORY) {
			data[l++] = 0x00 | op->operands[0].reg << 3 | op->operands[1].regs[0];
		} else {
			data[l++] = 0xc0 | op->operands[1].reg << 3 | op->operands[0].reg;
		}
	}
	return l;
}

static int opxchg(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	int mod_byte = 0;
	int reg = 0;
	int rm = 0;
	st32 offset = 0;

	if (op->operands[0].type & OT_MEMORY || op->operands[1].type & OT_MEMORY) {
		data[l++] = 0x87;
		if (op->operands[0].type & OT_MEMORY) {
			rm = op->operands[0].regs[0];
			offset = op->operands[0].offset * op->operands[0].offset_sign;
			reg = op->operands[1].reg;
		} else if (op->operands[1].type & OT_MEMORY) {
			rm = op->operands[1].regs[0];
			offset = op->operands[1].offset * op->operands[1].offset_sign;
			reg = op->operands[0].reg;
		}
		if (offset) {
			mod_byte = 1;
			if (offset < ST8_MIN || offset > ST8_MAX) {
				mod_byte = 2;
			}
		}
	} else {
		if (!((op->operands[0].type & ALL_SIZE) &
			    (op->operands[1].type & ALL_SIZE))) { // unmatched operand sizes
			return -1;
		}
		if (op->operands[0].reg == X86R_EAX &&
			!op->operands[0].extended &&
			!(op->operands[0].type & OT_BYTE) &&
			op->operands[1].type & OT_GPREG) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0x66;
			} else if (op->operands[0].type & OT_DWORD &&
				op->operands[1].extended) {
				data[l++] = 0x41;
			} else if (op->operands[0].type & OT_QWORD) {
				if (op->operands[1].extended) {
					data[l++] = 0x49;
				} else {
					data[l++] = 0x48;
				}
			}
			data[l++] = 0x90 + op->operands[1].reg;
			return l;
		} else if (op->operands[1].reg == X86R_EAX &&
			!op->operands[1].extended &&
			!(op->operands[1].type & OT_BYTE) &&
			op->operands[0].type & OT_GPREG) {
			if (op->operands[1].type & OT_WORD) {
				data[l++] = 0x66;
			} else if (op->operands[1].type & OT_DWORD &&
				op->operands[0].extended) {
				data[l++] = 0x41;
			} else if (op->operands[1].type & OT_QWORD) {
				if (op->operands[0].extended) {
					data[l++] = 0x49;
				} else {
					data[l++] = 0x48;
				}
			}
			data[l++] = 0x90 + op->operands[0].reg;
			return l;
		} else if (op->operands[0].type & OT_GPREG &&
			op->operands[1].type & OT_GPREG) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0x66;
			}
			ut8 rex = 0x40 | op->operands[0].extended | op->operands[1].extended << 2 | !!(op->operands[0].type & OT_QWORD) << 3;
			if (rex != 0x40) {
				data[l++] = rex;
			}
			if (op->operands[0].type & OT_BYTE) {
				data[l++] = 0x86;
			} else {
				data[l++] = 0x87;
			}
			mod_byte = 3;
			reg = op->operands[1].reg;
			rm = op->operands[0].reg;
		}
	}
	data[l++] = mod_byte << 6 | reg << 3 | rm;
	if (mod_byte > 0 && mod_byte < 3) {
		data[l++] = offset;
		if (mod_byte == 2) {
			data[l++] = offset >> 8;
			data[l++] = offset >> 16;
			data[l++] = offset >> 24;
		}
	}
	return l;
}

static int opcdqe(RzAsm *a, ut8 *data, const Opcode *op) {
	is_valid_registers(op);
	int l = 0;
	if (a->bits == 64) {
		data[l++] = 0x48;
	}
	data[l++] = 0x98;
	return l;
}

static int opfcmov(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	char *fcmov = op->mnemonic + strlen("fcmov");
	switch (op->operands_count) {
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			if (!strcmp(fcmov, "b")) {
				data[l++] = 0xda;
				data[l++] = 0xc0 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "e")) {
				data[l++] = 0xda;
				data[l++] = 0xc8 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "be")) {
				data[l++] = 0xda;
				data[l++] = 0xd0 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "u")) {
				data[l++] = 0xda;
				data[l++] = 0xd8 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "nb")) {
				data[l++] = 0xdb;
				data[l++] = 0xc0 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "ne")) {
				data[l++] = 0xdb;
				data[l++] = 0xc8 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "nbe")) {
				data[l++] = 0xdb;
				data[l++] = 0xd0 | op->operands[1].reg;
			} else if (!strcmp(fcmov, "nu")) {
				data[l++] = 0xdb;
				data[l++] = 0xd8 | op->operands[1].reg;
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opffree(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xdd;
			data[l++] = 0xc0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfrstor(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0xdd;
			data[l++] = 0x20 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfxch(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xd9;
		data[l++] = 0xc9;
		break;
	case 1:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd9;
			data[l++] = 0xc8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfucom(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xdd;
			data[l++] = 0xe0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	case 0:
		data[l++] = 0xdd;
		data[l++] = 0xe1;
		break;
	default:
		return -1;
	}
	return l;
}

static int opfucomp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xdd;
			data[l++] = 0xe8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	case 0:
		data[l++] = 0xdd;
		data[l++] = 0xe9;
		break;
	default:
		return -1;
	}
	return l;
}

static int opfaddp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xc0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xc1;
		break;
	default:
		return -1;
	}
	return l;
}

static int opfiadd(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfadd(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xc0 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xc0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opficom(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x10 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x10 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opficomp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x18 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x18 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfild(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xdf;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xdb;
				data[l++] = 0x00 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdf;
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfldcw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0xd9;
			data[l++] = 0x28 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfldenv(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0xd9;
			data[l++] = 0x20 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfbld(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_TBYTE) {
			data[l++] = 0xdf;
			data[l++] = 0x20 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfbstp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_TBYTE) {
			data[l++] = 0xdf;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfxrstor(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0xae;
			data[l++] = 0x08 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfxsave(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0xae;
			data[l++] = 0x00 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfist(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xdf;
				data[l++] = 0x10 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xdb;
				data[l++] = 0x10 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfistp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xdf;
				data[l++] = 0x18 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xdb;
				data[l++] = 0x18 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdf;
				data[l++] = 0x38 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfisttp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xdf;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xdb;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdd;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfstenv(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x9b;
			data[l++] = 0xd9;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfnstenv(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0xd9;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfdiv(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x30 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x30 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xf0 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xf8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfdivp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xf9;
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xf8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfidiv(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x30 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x30 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfdivr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x38 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x38 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xf8 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xf0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfdivrp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xf1;
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xf0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfidivr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x38 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x38 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfmul(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xc8 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xc8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfmulp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xc9;
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xc8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfimul(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x08 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfsub(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x20 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x20 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xe0 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xe8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfsubp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xe9;
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xe8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfisub(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x20 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x20 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfsubr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xd8;
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_QWORD) {
				data[l++] = 0xdc;
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL && op->operands[0].reg == 0 &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL) {
			data[l++] = 0xd8;
			data[l++] = 0xe8 | op->operands[1].reg;
		} else if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xdc;
			data[l++] = 0xe0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfsubrp(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 0:
		data[l++] = 0xde;
		data[l++] = 0xe1;
		break;
	case 2:
		if (op->operands[0].type & OT_FPUREG & ~OT_REGALL &&
			op->operands[1].type & OT_FPUREG & ~OT_REGALL && op->operands[1].reg == 0) {
			data[l++] = 0xde;
			data[l++] = 0xe0 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfisubr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			if (op->operands[0].type & OT_DWORD) {
				data[l++] = 0xda;
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else if (op->operands[0].type & OT_WORD) {
				data[l++] = 0xde;
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else {
				return -1;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfnstcw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0xd9;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfstcw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0x9b;
			data[l++] = 0xd9;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfnstsw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0xdd;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD &&
			op->operands[0].reg == X86R_AX) {
			data[l++] = 0xdf;
			data[l++] = 0xe0;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfstsw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0x9b;
			data[l++] = 0xdd;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_WORD &&
			op->operands[0].reg == X86R_AX) {
			data[l++] = 0x9b;
			data[l++] = 0xdf;
			data[l++] = 0xe0;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfnsave(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0xdd;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opfsave(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0x9b;
			data[l++] = 0xdd;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int oplldt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x0f;
			data[l++] = 0x00;
			if (op->operands[0].type & OT_MEMORY) {
				data[l++] = 0x10 | op->operands[0].regs[0];
			} else {
				data[l++] = 0xd0 | op->operands[0].reg;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int oplmsw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			if (op->operands[0].type & OT_MEMORY) {
				data[l++] = 0x30 | op->operands[0].regs[0];
			} else {
				data[l++] = 0xf0 | op->operands[0].reg;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int oplgdt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			data[l++] = 0x10 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int oplidt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			data[l++] = 0x18 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opsgdt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			data[l++] = 0x00 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opstmxcsr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0x0f;
			data[l++] = 0xae;
			data[l++] = 0x18 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opstr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_WORD) {
			data[l++] = 0x0f;
			data[l++] = 0x00;
			data[l++] = 0x08 | op->operands[0].regs[0];
		} else if (op->operands[0].type & OT_GPREG &&
			op->operands[0].type & OT_DWORD) {
			data[l++] = 0x0f;
			data[l++] = 0x00;
			data[l++] = 0xc8 | op->operands[0].reg;
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opsidt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x0f;
			data[l++] = 0x01;
			data[l++] = 0x08 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opsldt(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (a->bits == 64) {
			data[l++] = 0x48;
		}
		data[l++] = 0x0f;
		data[l++] = 0x00;
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x00 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xc0 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opsmsw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (a->bits == 64) {
			data[l++] = 0x48;
		}
		data[l++] = 0x0f;
		data[l++] = 0x01;
		if (op->operands[0].type & OT_MEMORY) {
			data[l++] = 0x20 | op->operands[0].regs[0];
		} else {
			data[l++] = 0xe0 | op->operands[0].reg;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opverr(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x0f;
			data[l++] = 0x00;
			if (op->operands[0].type & OT_MEMORY) {
				data[l++] = 0x20 | op->operands[0].regs[0];
			} else {
				data[l++] = 0xe0 | op->operands[0].reg;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opverw(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_WORD) {
			data[l++] = 0x0f;
			data[l++] = 0x00;
			if (op->operands[0].type & OT_MEMORY) {
				data[l++] = 0x28 | op->operands[0].regs[0];
			} else {
				data[l++] = 0xe8 | op->operands[0].reg;
			}
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opvmclear(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_QWORD) {
			data[l++] = 0x66;
			data[l++] = 0x0f;
			data[l++] = 0xc7;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opvmon(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_QWORD) {
			data[l++] = 0xf3;
			data[l++] = 0x0f;
			data[l++] = 0xc7;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opvmptrld(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_QWORD) {
			data[l++] = 0x0f;
			data[l++] = 0xc7;
			data[l++] = 0x30 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

static int opvmptrst(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	switch (op->operands_count) {
	case 1:
		if (op->operands[0].type & OT_MEMORY &&
			op->operands[0].type & OT_QWORD) {
			data[l++] = 0x0f;
			data[l++] = 0xc7;
			data[l++] = 0x38 | op->operands[0].regs[0];
		} else {
			return -1;
		}
		break;
	default:
		return -1;
	}
	return l;
}

typedef struct lookup_t {
	char mnemonic[12];
	int only_x32;
	int (*opdo)(RzAsm *, ut8 *, const Opcode *);
	ut64 opcode;
	int size;
} LookupTable;

LookupTable oplookup[] = {
	{ "aaa", 0, NULL, 0x37, 1 },
	{ "aad", 0, opaad, 0 },
	{ "aam", 0, opaam, 0 },
	{ "aas", 0, NULL, 0x3f, 1 },
	{ "adc", 0, &opadc, 0 },
	{ "add", 0, &opadd, 0 },
	{ "adx", 0, NULL, 0xd4, 1 },
	{ "amx", 0, NULL, 0xd5, 1 },
	{ "and", 0, &opand, 0 },
	{ "bsf", 0, &opbs, 0 },
	{ "bsr", 0, &opbs, 0 },
	{ "bswap", 0, &opbswap, 0 },
	{ "call", 0, &opcall, 0 },
	{ "cbw", 0, NULL, 0x6698, 2 },
	{ "cdq", 0, NULL, 0x99, 1 },
	{ "cdqe", 0, &opcdqe, 0 },
	{ "cwde", 0, &opcdqe, 0 },
	{ "clc", 0, NULL, 0xf8, 1 },
	{ "cld", 0, NULL, 0xfc, 1 },
	{ "clflush", 0, &opclflush, 0 },
	{ "clgi", 0, NULL, 0x0f01dd, 3 },
	{ "cli", 0, NULL, 0xfa, 1 },
	{ "clts", 0, NULL, 0x0f06, 2 },
	{ "cmc", 0, NULL, 0xf5, 1 },
	{ "cmovo", 0, &opcmov, 0 },
	{ "cmovno", 0, &opcmov, 0 },
	{ "cmovb", 0, &opcmov, 0 },
	{ "cmovc", 0, &opcmov, 0 },
	{ "cmovnae", 0, &opcmov, 0 },
	{ "cmovae", 0, &opcmov, 0 },
	{ "cmovnb", 0, &opcmov, 0 },
	{ "cmovnc", 0, &opcmov, 0 },
	{ "cmove", 0, &opcmov, 0 },
	{ "cmovz", 0, &opcmov, 0 },
	{ "cmovne", 0, &opcmov, 0 },
	{ "cmovnz", 0, &opcmov, 0 },
	{ "cmovbe", 0, &opcmov, 0 },
	{ "cmovna", 0, &opcmov, 0 },
	{ "cmova", 0, &opcmov, 0 },
	{ "cmovnbe", 0, &opcmov, 0 },
	{ "cmovne", 0, &opcmov, 0 },
	{ "cmovnz", 0, &opcmov, 0 },
	{ "cmovs", 0, &opcmov, 0 },
	{ "cmovns", 0, &opcmov, 0 },
	{ "cmovp", 0, &opcmov, 0 },
	{ "cmovpe", 0, &opcmov, 0 },
	{ "cmovnp", 0, &opcmov, 0 },
	{ "cmovpo", 0, &opcmov, 0 },
	{ "cmovl", 0, &opcmov, 0 },
	{ "cmovnge", 0, &opcmov, 0 },
	{ "cmovge", 0, &opcmov, 0 },
	{ "cmovnl", 0, &opcmov, 0 },
	{ "cmovle", 0, &opcmov, 0 },
	{ "cmovng", 0, &opcmov, 0 },
	{ "cmovg", 0, &opcmov, 0 },
	{ "cmovnle", 0, &opcmov, 0 },
	{ "cmp", 0, &opcmp, 0 },
	{ "cmpsb", 0, NULL, 0xa6, 1 },
	{ "cmpsd", 0, NULL, 0xa7, 1 },
	{ "cmpsw", 0, NULL, 0x66a7, 2 },
	{ "cpuid", 0, NULL, 0x0fa2, 2 },
	{ "cwd", 0, NULL, 0x6699, 2 },
	{ "cwde", 0, NULL, 0x98, 1 },
	{ "daa", 0, NULL, 0x27, 1 },
	{ "das", 0, NULL, 0x2f, 1 },
	{ "dec", 0, &opdec, 0 },
	{ "div", 0, &opdiv, 0 },
	{ "emms", 0, NULL, 0x0f77, 2 },
	{ "endbr32", 0, endbr32, 0 },
	{ "endbr64", 0, endbr64, 0 },
	{ "f2xm1", 0, NULL, 0xd9f0, 2 },
	{ "fabs", 0, NULL, 0xd9e1, 2 },
	{ "fadd", 0, &opfadd, 0 },
	{ "faddp", 0, &opfaddp, 0 },
	{ "fbld", 0, &opfbld, 0 },
	{ "fbstp", 0, &opfbstp, 0 },
	{ "fchs", 0, NULL, 0xd9e0, 2 },
	{ "fclex", 0, NULL, 0x9bdbe2, 3 },
	{ "fcmovb", 0, &opfcmov, 0 },
	{ "fcmove", 0, &opfcmov, 0 },
	{ "fcmovbe", 0, &opfcmov, 0 },
	{ "fcmovu", 0, &opfcmov, 0 },
	{ "fcmovnb", 0, &opfcmov, 0 },
	{ "fcmovne", 0, &opfcmov, 0 },
	{ "fcmovnbe", 0, &opfcmov, 0 },
	{ "fcmovnu", 0, &opfcmov, 0 },
	{ "fcos", 0, NULL, 0xd9ff, 2 },
	{ "fdecstp", 0, NULL, 0xd9f6, 2 },
	{ "fdiv", 0, &opfdiv, 0 },
	{ "fdivp", 0, &opfdivp, 0 },
	{ "fdivr", 0, &opfdivr, 0 },
	{ "fdivrp", 0, &opfdivrp, 0 },
	{ "femms", 0, NULL, 0x0f0e, 2 },
	{ "ffree", 0, &opffree, 0 },
	{ "fiadd", 0, &opfiadd, 0 },
	{ "ficom", 0, &opficom, 0 },
	{ "ficomp", 0, &opficomp, 0 },
	{ "fidiv", 0, &opfidiv, 0 },
	{ "fidivr", 0, &opfidivr, 0 },
	{ "fild", 0, &opfild, 0 },
	{ "fimul", 0, &opfimul, 0 },
	{ "fincstp", 0, NULL, 0xd9f7, 2 },
	{ "finit", 0, NULL, 0x9bdbe3, 3 },
	{ "fist", 0, &opfist, 0 },
	{ "fistp", 0, &opfistp, 0 },
	{ "fisttp", 0, &opfisttp, 0 },
	{ "fisub", 0, &opfisub, 0 },
	{ "fisubr", 0, &opfisubr, 0 },
	{ "fld1", 0, NULL, 0xd9e8, 2 },
	{ "fldcw", 0, &opfldcw, 0 },
	{ "fldenv", 0, &opfldenv, 0 },
	{ "fldl2t", 0, NULL, 0xd9e9, 2 },
	{ "fldl2e", 0, NULL, 0xd9ea, 2 },
	{ "fldlg2", 0, NULL, 0xd9ec, 2 },
	{ "fldln2", 0, NULL, 0xd9ed, 2 },
	{ "fldpi", 0, NULL, 0xd9eb, 2 },
	{ "fldz", 0, NULL, 0xd9ee, 2 },
	{ "fmul", 0, &opfmul, 0 },
	{ "fmulp", 0, &opfmulp, 0 },
	{ "fnclex", 0, NULL, 0xdbe2, 2 },
	{ "fninit", 0, NULL, 0xdbe3, 2 },
	{ "fnop", 0, NULL, 0xd9d0, 2 },
	{ "fnsave", 0, &opfnsave, 0 },
	{ "fnstcw", 0, &opfnstcw, 0 },
	{ "fnstenv", 0, &opfnstenv, 0 },
	{ "fnstsw", 0, &opfnstsw, 0 },
	{ "fpatan", 0, NULL, 0xd9f3, 2 },
	{ "fprem", 0, NULL, 0xd9f8, 2 },
	{ "fprem1", 0, NULL, 0xd9f5, 2 },
	{ "fptan", 0, NULL, 0xd9f2, 2 },
	{ "frndint", 0, NULL, 0xd9fc, 2 },
	{ "frstor", 0, &opfrstor, 0 },
	{ "fsave", 0, &opfsave, 0 },
	{ "fscale", 0, NULL, 0xd9fd, 2 },
	{ "fsin", 0, NULL, 0xd9fe, 2 },
	{ "fsincos", 0, NULL, 0xd9fb, 2 },
	{ "fsqrt", 0, NULL, 0xd9fa, 2 },
	{ "fstcw", 0, &opfstcw, 0 },
	{ "fstenv", 0, &opfstenv, 0 },
	{ "fstsw", 0, &opfstsw, 0 },
	{ "fsub", 0, &opfsub, 0 },
	{ "fsubp", 0, &opfsubp, 0 },
	{ "fsubr", 0, &opfsubr, 0 },
	{ "fsubrp", 0, &opfsubrp, 0 },
	{ "ftst", 0, NULL, 0xd9e4, 2 },
	{ "fucom", 0, &opfucom, 0 },
	{ "fucomp", 0, &opfucomp, 0 },
	{ "fucompp", 0, NULL, 0xdae9, 2 },
	{ "fwait", 0, NULL, 0x9b, 1 },
	{ "fxam", 0, NULL, 0xd9e5, 2 },
	{ "fxch", 0, &opfxch, 0 },
	{ "fxrstor", 0, &opfxrstor, 0 },
	{ "fxsave", 0, &opfxsave, 0 },
	{ "fxtract", 0, NULL, 0xd9f4, 2 },
	{ "fyl2x", 0, NULL, 0xd9f1, 2 },
	{ "fyl2xp1", 0, NULL, 0xd9f9, 2 },
	{ "getsec", 0, NULL, 0x0f37, 2 },
	{ "hlt", 0, NULL, 0xf4, 1 },
	{ "idiv", 0, &opidiv, 0 },
	{ "imul", 0, &opimul, 0 },
	{ "in", 0, &opin, 0 },
	{ "inc", 0, &opinc, 0 },
	{ "ins", 0, NULL, 0x6d, 1 },
	{ "insb", 0, NULL, 0x6c, 1 },
	{ "insd", 0, NULL, 0x6d, 1 },
	{ "insw", 0, NULL, 0x666d, 2 },
	{ "int", 0, &opint, 0 },
	{ "int1", 0, NULL, 0xf1, 1 },
	{ "int3", 0, NULL, 0xcc, 1 },
	{ "into", 0, NULL, 0xce, 1 },
	{ "invd", 0, NULL, 0x0f08, 2 },
	{ "iret", 0, NULL, 0x66cf, 2 },
	{ "iretd", 0, NULL, 0xcf, 1 },
	{ "ja", 0, &opjc, 0 },
	{ "jae", 0, &opjc, 0 },
	{ "jb", 0, &opjc, 0 },
	{ "jbe", 0, &opjc, 0 },
	{ "jc", 0, &opjc, 0 },
	{ "je", 0, &opjc, 0 },
	{ "jg", 0, &opjc, 0 },
	{ "jge", 0, &opjc, 0 },
	{ "jl", 0, &opjc, 0 },
	{ "jle", 0, &opjc, 0 },
	{ "jmp", 0, &opjc, 0 },
	{ "jna", 0, &opjc, 0 },
	{ "jnae", 0, &opjc, 0 },
	{ "jnb", 0, &opjc, 0 },
	{ "jnbe", 0, &opjc, 0 },
	{ "jnc", 0, &opjc, 0 },
	{ "jne", 0, &opjc, 0 },
	{ "jng", 0, &opjc, 0 },
	{ "jnge", 0, &opjc, 0 },
	{ "jnl", 0, &opjc, 0 },
	{ "jnle", 0, &opjc, 0 },
	{ "jno", 0, &opjc, 0 },
	{ "jnp", 0, &opjc, 0 },
	{ "jns", 0, &opjc, 0 },
	{ "jnz", 0, &opjc, 0 },
	{ "jo", 0, &opjc, 0 },
	{ "jp", 0, &opjc, 0 },
	{ "jpe", 0, &opjc, 0 },
	{ "jpo", 0, &opjc, 0 },
	{ "js", 0, &opjc, 0 },
	{ "jz", 0, &opjc, 0 },
	{ "lahf", 0, NULL, 0x9f, 1 },
	{ "lea", 0, &oplea, 0 },
	{ "leave", 0, NULL, 0xc9, 1 },
	{ "les", 0, &oples, 0 },
	{ "lfence", 0, NULL, 0x0faee8, 3 },
	{ "lgdt", 0, &oplgdt, 0 },
	{ "lidt", 0, &oplidt, 0 },
	{ "lldt", 0, &oplldt, 0 },
	{ "lmsw", 0, &oplmsw, 0 },
	{ "lodsb", 0, NULL, 0xac, 1 },
	{ "lodsd", 0, NULL, 0xad, 1 },
	{ "lodsw", 0, NULL, 0x66ad, 2 },
	{ "loop", 0, &oploop, 0 },
	{ "mfence", 0, NULL, 0x0faef0, 3 },
	{ "monitor", 0, NULL, 0x0f01c8, 3 },
	{ "mov", 0, &opmov, 0 },
	{ "movsb", 0, NULL, 0xa4, 1 },
	{ "movsd", 0, NULL, 0xa5, 1 },
	{ "movsw", 0, NULL, 0x66a5, 2 },
	{ "movzx", 0, &opmovx, 0 },
	{ "movsx", 0, &opmovx, 0 },
	{ "movabs", 0, &opmovabs, 0 },
	{ "mul", 0, &opmul, 0 },
	{ "mwait", 0, NULL, 0x0f01c9, 3 },
	{ "neg", 0, &opneg, 0 },
	{ "nop", 0, NULL, 0x90, 1 },
	{ "not", 0, &opnot, 0 },
	{ "or", 0, &opor, 0 },
	{ "out", 0, &opout, 0 },
	{ "outsb", 0, NULL, 0x6e, 1 },
	{ "outs", 0, NULL, 0x6f, 1 },
	{ "outsd", 0, NULL, 0x6f, 1 },
	{ "outsw", 0, NULL, 0x666f, 2 },
	{ "pop", 0, &oppop, 0 },
	{ "popa", 1, NULL, 0x61, 1 },
	{ "popad", 1, NULL, 0x61, 1 },
	{ "popal", 1, NULL, 0x61, 1 },
	{ "popaw", 1, NULL, 0x6661, 2 },
	{ "popfd", 1, NULL, 0x9d, 1 },
	{ "prefetch", 0, NULL, 0x0f0d, 2 },
	{ "push", 0, &oppush, 0 },
	{ "pusha", 1, NULL, 0x60, 1 },
	{ "pushad", 1, NULL, 0x60, 1 },
	{ "pushal", 1, NULL, 0x60, 1 },
	{ "pushfd", 0, NULL, 0x9c, 1 },
	{ "rcl", 0, &process_group_2, 0 },
	{ "rcr", 0, &process_group_2, 0 },
	{ "rep", 0, &oprep, 0 },
	{ "repe", 0, &oprep, 0 },
	{ "repne", 0, &oprep, 0 },
	{ "repz", 0, &oprep, 0 },
	{ "repnz", 0, &oprep, 0 },
	{ "rdmsr", 0, NULL, 0x0f32, 2 },
	{ "rdpmc", 0, NULL, 0x0f33, 2 },
	{ "rdtsc", 0, NULL, 0x0f31, 2 },
	{ "rdtscp", 0, NULL, 0x0f01f9, 3 },
	{ "ret", 0, &opret, 0 },
	{ "retf", 0, &opretf, 0 },
	{ "retw", 0, NULL, 0x66c3, 2 },
	{ "rol", 0, &process_group_2, 0 },
	{ "ror", 0, &process_group_2, 0 },
	{ "rsm", 0, NULL, 0x0faa, 2 },
	{ "sahf", 0, NULL, 0x9e, 1 },
	{ "sal", 0, &process_group_2, 0 },
	{ "salc", 0, NULL, 0xd6, 1 },
	{ "sar", 0, &process_group_2, 0 },
	{ "sbb", 0, &opsbb, 0 },
	{ "scasb", 0, NULL, 0xae, 1 },
	{ "scasd", 0, NULL, 0xaf, 1 },
	{ "scasw", 0, NULL, 0x66af, 2 },
	{ "seto", 0, &opset, 0 },
	{ "setno", 0, &opset, 0 },
	{ "setb", 0, &opset, 0 },
	{ "setnae", 0, &opset, 0 },
	{ "setc", 0, &opset, 0 },
	{ "setnb", 0, &opset, 0 },
	{ "setae", 0, &opset, 0 },
	{ "setnc", 0, &opset, 0 },
	{ "setz", 0, &opset, 0 },
	{ "sete", 0, &opset, 0 },
	{ "setnz", 0, &opset, 0 },
	{ "setne", 0, &opset, 0 },
	{ "setbe", 0, &opset, 0 },
	{ "setna", 0, &opset, 0 },
	{ "setnbe", 0, &opset, 0 },
	{ "seta", 0, &opset, 0 },
	{ "sets", 0, &opset, 0 },
	{ "setns", 0, &opset, 0 },
	{ "setp", 0, &opset, 0 },
	{ "setpe", 0, &opset, 0 },
	{ "setnp", 0, &opset, 0 },
	{ "setpo", 0, &opset, 0 },
	{ "setl", 0, &opset, 0 },
	{ "setnge", 0, &opset, 0 },
	{ "setnl", 0, &opset, 0 },
	{ "setge", 0, &opset, 0 },
	{ "setle", 0, &opset, 0 },
	{ "setng", 0, &opset, 0 },
	{ "setnle", 0, &opset, 0 },
	{ "setg", 0, &opset, 0 },
	{ "sfence", 0, NULL, 0x0faef8, 3 },
	{ "sgdt", 0, &opsgdt, 0 },
	{ "shl", 0, &process_group_2, 0 },
	{ "shr", 0, &process_group_2, 0 },
	{ "sidt", 0, &opsidt, 0 },
	{ "sldt", 0, &opsldt, 0 },
	{ "smsw", 0, &opsmsw, 0 },
	{ "stc", 0, NULL, 0xf9, 1 },
	{ "std", 0, NULL, 0xfd, 1 },
	{ "stgi", 0, NULL, 0x0f01dc, 3 },
	{ "sti", 0, NULL, 0xfb, 1 },
	{ "stmxcsr", 0, &opstmxcsr, 0 },
	{ "stosb", 0, &opstos, 0 },
	{ "stosd", 0, &opstos, 0 },
	{ "stosw", 0, &opstos, 0 },
	{ "str", 0, &opstr, 0 },
	{ "sub", 0, &opsub, 0 },
	{ "swapgs", 0, NULL, 0x0f1ff8, 3 },
	{ "syscall", 0, NULL, 0x0f05, 2 },
	{ "sysenter", 0, NULL, 0x0f34, 2 },
	{ "sysexit", 0, NULL, 0x0f35, 2 },
	{ "sysret", 0, NULL, 0x0f07, 2 },
	{ "ud2", 0, NULL, 0x0f0b, 2 },
	{ "verr", 0, &opverr, 0 },
	{ "verw", 0, &opverw, 0 },
	{ "vmcall", 0, NULL, 0x0f01c1, 3 },
	{ "vmclear", 0, &opvmclear, 0 },
	{ "vmlaunch", 0, NULL, 0x0f01c2, 3 },
	{ "vmload", 0, NULL, 0x0f01da, 3 },
	{ "vmmcall", 0, NULL, 0x0f01d9, 3 },
	{ "vmptrld", 0, &opvmptrld, 0 },
	{ "vmptrst", 0, &opvmptrst, 0 },
	{ "vmresume", 0, NULL, 0x0f01c3, 3 },
	{ "vmrun", 0, NULL, 0x0f01d8, 3 },
	{ "vmsave", 0, NULL, 0x0f01db, 3 },
	{ "vmxoff", 0, NULL, 0x0f01c4, 3 },
	{ "vmxon", 0, &opvmon, 0 },
	{ "vzeroall", 0, NULL, 0xc5fc77, 3 },
	{ "vzeroupper", 0, NULL, 0xc5f877, 3 },
	{ "wait", 0, NULL, 0x9b, 1 },
	{ "wbinvd", 0, NULL, 0x0f09, 2 },
	{ "wrmsr", 0, NULL, 0x0f30, 2 },
	{ "xadd", 0, &opxadd, 0 },
	{ "xchg", 0, &opxchg, 0 },
	{ "xgetbv", 0, NULL, 0x0f01d0, 3 },
	{ "xlatb", 0, NULL, 0xd7, 1 },
	{ "xor", 0, &opxor, 0 },
	{ "xsetbv", 0, NULL, 0x0f01d1, 3 },
	{ "test", 0, &optest, 0 },
	{ "null", 0, NULL, 0, 0 }
};

static x86newTokenType getToken(const char *str, size_t *begin, size_t *end) {
	if (*begin > strlen(str)) {
		return TT_EOF;
	}
	// Skip whitespace
	while (begin && str[*begin] && isspace((ut8)str[*begin])) {
		++(*begin);
	}

	if (!str[*begin]) { // null byte
		*end = *begin;
		return TT_EOF;
	}
	if (isalpha((ut8)str[*begin])) { // word token
		*end = *begin;
		while (end && str[*end] && isalnum((ut8)str[*end])) {
			++(*end);
		}
		return TT_WORD;
	}
	if (isdigit((ut8)str[*begin])) { // number token
		*end = *begin;
		while (end && isalnum((ut8)str[*end])) { // accept alphanumeric characters, because hex.
			++(*end);
		}
		return TT_NUMBER;
	} else { // special character: [, ], +, *, ...
		*end = *begin + 1;
		return TT_SPECIAL;
	}
}

static bool is_xmm_register(const char *token) {
	// check xmm0..xmm15
	if (!rz_str_ncasecmp("xmm", token, 3)) {
		int n = atoi(token + 3);
		return (n >= 0 && n <= 15);
	}
	return false;
}

static bool is_mm_register(const char *token) {
	if (!rz_str_ncasecmp("mm", token, 2)) {
		const bool parn = token[2] == '(';
		if (parn) {
			token++;
		}
		if (isdigit(token[2]) && !isdigit(token[3])) {
			int n = token[2];
			if (n >= '0' && n <= '7') {
				if (parn) {
					if (token[3] != ')') {
						return false;
					}
				}
				return true;
			}
		}
	}
	return false;
}

static bool is_st_register(const char *token) {
	if (!rz_str_ncasecmp("st", token, 2)) {
		const bool parn = token[2] == '(';
		if (parn) {
			token++;
		}
		if (isdigit(token[2]) && !isdigit(token[3])) {
			int n = token[2];
			if (n >= '0' && n <= '7') {
				if (parn) {
					if (token[3] != ')') {
						return false;
					}
				}
				return true;
			}
		}
	}
	return false;
}

/**
 * Get the register at position pos in str. Increase pos afterwards.
 */
static Register parseReg(RzAsm *a, const char *str, size_t *pos, ut32 *type) {
	int i;
	// Must be the same order as in enum register_t
	const char *regs[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "eip", NULL };
	const char *regsext[] = { "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", NULL };
	const char *regs8[] = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", NULL };
	const char *regs16[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", NULL };
	const char *regs64[] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "rip", NULL };
	const char *regs64ext[] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", NULL };
	const char *sregs[] = { "es", "cs", "ss", "ds", "fs", "gs", NULL };
	const char *cregs[] = { "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", NULL };
	const char *dregs[] = { "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", NULL };

	// Get token (especially the length)
	size_t nextpos, length;
	const char *token;
	if (getToken(str, pos, &nextpos) != TT_WORD) {
		/* We expect a word, anything else means invalid register */
		return X86R_UNDEFINED;
	}

	token = str + *pos;
	length = nextpos - *pos;
	*pos = nextpos;

	// General purpose registers
	if (length == 3 && token[0] == 'e') {
		for (i = 0; regs[i]; i++) {
			if (!rz_str_ncasecmp(regs[i], token, length)) {
				*type = (OT_GPREG & OT_REG(i)) | OT_DWORD;
				return i;
			}
		}
	}
	// Control registers
	if (length == 3 && token[0] == 'c') {
		for (i = 0; cregs[i]; i++) {
			if (!rz_str_ncasecmp(cregs[i], token, length)) {
				*type = (OT_CONTROLREG & OT_REG(i)) | OT_DWORD;
				return i;
			}
		}
	}
	// Debug registers
	if (length == 3 && token[0] == 'd') {
		for (i = 0; dregs[i]; i++) {
			if (!rz_str_ncasecmp(dregs[i], token, length)) {
				*type = (OT_DEBUGREG & OT_REG(i)) | OT_DWORD;
				return i;
			}
		}
	}
	if (length == 2) {
		if (token[1] == 'l' || token[1] == 'h') {
			for (i = 0; regs8[i]; i++) {
				if (!rz_str_ncasecmp(regs8[i], token, length)) {
					*type = (OT_GPREG & OT_REG(i)) | OT_BYTE;
					return i;
				}
			}
		}
		for (i = 0; regs16[i]; i++) {
			if (!rz_str_ncasecmp(regs16[i], token, length)) {
				*type = (OT_GPREG & OT_REG(i)) | OT_WORD;
				return i;
			}
		}
		// This isn't working properly yet
		for (i = 0; sregs[i]; i++) {
			if (!rz_str_ncasecmp(sregs[i], token, length)) {
				*type = (OT_SEGMENTREG & OT_REG(i)) | OT_WORD;
				return i;
			}
		}
	}
	if (token[0] == 'r') {
		for (i = 0; regs64[i]; i++) {
			if (!rz_str_ncasecmp(regs64[i], token, length)) {
				*type = (OT_GPREG & OT_REG(i)) | OT_QWORD;
				a->bits = 64;
				return i;
			}
		}
		for (i = 0; regs64ext[i]; i++) {
			if (!rz_str_ncasecmp(regs64ext[i], token, length)) {
				*type = (OT_GPREG & OT_REG(i)) | OT_QWORD;
				a->bits = 64;
				return i + 9;
			}
		}
		for (i = 0; regsext[i]; i++) {
			if (!rz_str_ncasecmp(regsext[i], token, length)) {
				*type = (OT_GPREG & OT_REG(i)) | OT_DWORD;
				if (a->bits < 32) {
					a->bits = 32;
				}
				return i + 9;
			}
		}
	}
	// Extended registers
	if (is_st_register(token)) {
		*type = (OT_FPUREG & ~OT_REGALL);
		*pos = 2;
	}
	if (is_mm_register(token)) {
		*type = (OT_MMXREG & ~OT_REGALL);
		*pos = 2;
	}
	if (is_xmm_register(token)) {
		*type = (OT_XMMREG & ~OT_REGALL);
		*pos = 3;
	}
	// Now read number, possibly with parentheses
	if (*type & (OT_FPUREG | OT_MMXREG | OT_XMMREG) & ~OT_REGALL) {
		Register reg = X86R_UNDEFINED;
		// pass by '(',if there is one
		if (getToken(token, pos, &nextpos) == TT_SPECIAL && token[*pos] == '(') {
			*pos = nextpos;
		}
		// read number
		// const int maxreg = (a->bits == 64) ? 15 : 7;
		if (getToken(token, pos, &nextpos) != TT_NUMBER) {
			RZ_LOG_ERROR("assembler: x86.nz: expected register number but found '%s'\n", str + *pos);
			return X86R_UNDEFINED;
		}
		reg = getnum(a, token + *pos);
		// st and mm go up to 7, xmm up to 15
		if ((reg > 15) || ((*type & (OT_FPUREG | OT_MMXREG) & ~OT_REGALL) && reg > 7)) {
			RZ_LOG_ERROR("assembler: x86.nz: register index is too large\n");
			return X86R_UNDEFINED;
		}
		*pos = nextpos;

		// pass by ')'
		if (getToken(token, pos, &nextpos) == TT_SPECIAL && token[*pos] == ')') {
			*pos = nextpos;
		}
		*type |= (OT_REG(reg) & ~OT_REGTYPE);
		return reg;
	}
	return X86R_UNDEFINED;
}

static void parse_segment_offset(RzAsm *a, const char *str, size_t *pos,
	Operand *op, int reg_index) {
	int nextpos = *pos;
	char *c = strchr(str + nextpos, ':');
	if (c) {
		nextpos++; // Skip the ':'
		c = strchr(str + nextpos, '[');
		if (c) {
			nextpos++;
		} // Skip the '['

		// Assign registers to match behaviour of OT_MEMORY type
		op->regs[reg_index] = op->reg;
		op->type |= OT_MEMORY;
		op->offset_sign = 1;
		char *p = strchr(str + nextpos, '-');
		if (p) {
			op->offset_sign = -1;
			nextpos++;
		}
		op->scale[reg_index] = getnum(a, str + nextpos);
		op->offset = op->scale[reg_index];
	}
}
// Parse operand
static int parseOperand(RzAsm *a, const char *str, Operand *op, bool isrepop) {
	size_t pos, nextpos = 0;
	x86newTokenType last_type;
	int size_token = 1;
	bool explicit_size = false;
	int reg_index = 0;
	// Reset type
	op->type = 0;
	// Consume tokens denoting the operand size
	while (size_token) {
		pos = nextpos;
		last_type = getToken(str, &pos, &nextpos);

		// Token may indicate size: then skip
		if (!rz_str_ncasecmp(str + pos, "ptr", 3)) {
			continue;
		} else if (!rz_str_ncasecmp(str + pos, "byte", 4)) {
			op->type |= OT_MEMORY | OT_BYTE;
			op->dest_size = OT_BYTE;
			explicit_size = true;
		} else if (!rz_str_ncasecmp(str + pos, "word", 4)) {
			op->type |= OT_MEMORY | OT_WORD;
			op->dest_size = OT_WORD;
			explicit_size = true;
		} else if (!rz_str_ncasecmp(str + pos, "dword", 5)) {
			op->type |= OT_MEMORY | OT_DWORD;
			op->dest_size = OT_DWORD;
			explicit_size = true;
		} else if (!rz_str_ncasecmp(str + pos, "qword", 5)) {
			op->type |= OT_MEMORY | OT_QWORD;
			op->dest_size = OT_QWORD;
			explicit_size = true;
		} else if (!rz_str_ncasecmp(str + pos, "oword", 5)) {
			op->type |= OT_MEMORY | OT_OWORD;
			op->dest_size = OT_OWORD;
			explicit_size = true;
		} else if (!rz_str_ncasecmp(str + pos, "tbyte", 5)) {
			op->type |= OT_MEMORY | OT_TBYTE;
			op->dest_size = OT_TBYTE;
			explicit_size = true;
		} else { // the current token doesn't denote a size
			size_token = 0;
		}
	}

	// Next token: register, immediate, or '['
	if (str[pos] == '[') {
		// Don't care about size, if none is given.
		if (!op->type) {
			op->type = OT_MEMORY;
		}
		// At the moment, we only accept plain linear combinations:
		// part := address | [factor *] register
		// address := part {+ part}*
		op->offset = op->scale[0] = op->scale[1] = 0;

		ut64 temp = 1;
		Register reg = X86R_UNDEFINED;
		bool first_reg = true;
		while (str[pos] != ']') {
			if (pos > nextpos) {
				// RZ_LOG_ERROR("assembler: x86.nz: failed to parse instruction\n");
				break;
			}
			pos = nextpos;
			if (!str[pos]) {
				break;
			}
			last_type = getToken(str, &pos, &nextpos);

			if (last_type == TT_SPECIAL) {
				if (str[pos] == '+' || str[pos] == '-' || str[pos] == ']') {
					if (reg != X86R_UNDEFINED) {
						if (reg_index < 2) {
							op->regs[reg_index] = reg;
							op->scale[reg_index] = temp;
						}
						++reg_index;
					} else {
						op->offset += temp;
						if (reg_index < 2) {
							op->regs[reg_index] = X86R_UNDEFINED;
						}
					}
					temp = 1;
					reg = X86R_UNDEFINED;
				} else if (str[pos] == '*') {
					// go to ], + or - to get scale

					// Something to do here?
					// Seems we are just ignoring '*' or assuming it implicitly.
				}
			} else if (last_type == TT_WORD) {
				ut32 reg_type = 0;

				// We can't multiply registers
				if (reg != X86R_UNDEFINED) {
					op->type = 0; // Make the result invalid
				}

				// Reset nextpos: parseReg wants to parse from the beginning
				nextpos = pos;
				reg = parseReg(a, str, &nextpos, &reg_type);

				if (first_reg) {
					op->extended = false;
					if (reg > 8) {
						op->extended = true;
						op->reg = reg - 9;
					} else {
						op->reg = reg;
					}
					first_reg = false;
				} else if (reg > 8) {
					op->reg = reg - 9;
				}
				if (reg_type & OT_REGTYPE & OT_SEGMENTREG) {
					op->reg = reg;
					op->type = reg_type;
					parse_segment_offset(a, str, &nextpos, op, reg_index);
					return nextpos;
				}

				// Still going to need to know the size if not specified
				if (!explicit_size) {
					op->type |= reg_type;
				}
				op->reg_size = reg_type;
				op->explicit_size = explicit_size;

				// Addressing only via general purpose registers
				if (!(reg_type & OT_GPREG)) {
					op->type = 0; // Make the result invalid
				}
			} else {
				char *p = strchr(str, '+');
				op->offset_sign = 1;
				if (!p) {
					p = strchr(str, '-');
					if (p) {
						op->offset_sign = -1;
					}
				}
				// with SIB notation, we need to consider the right sign
				char *plus = strchr(str, '+');
				char *minus = strchr(str, '-');
				char *closeB = strchr(str, ']');
				if (plus && minus && plus < closeB && minus < closeB) {
					op->offset_sign = -1;
				}
				// If there's a scale, we don't want to parse out the
				// scale with the offset (scale + offset) otherwise the scale
				// will be the sum of the two. This splits the numbers
				char *tmp;
				tmp = malloc(strlen(str + pos) + 1);
				strcpy(tmp, str + pos);
				strtok(tmp, "+-");
				st64 read = getnum(a, tmp);
				free(tmp);
				temp *= read;
			}
		}
	} else if (last_type == TT_WORD) { // register
		nextpos = pos;

		if (isrepop) {
			op->is_good_flag = false;
			strncpy(op->rep_op, str, MAX_REPOP_LENGTH - 1);
			op->rep_op[MAX_REPOP_LENGTH - 1] = '\0';
			return nextpos;
		}

		op->reg = parseReg(a, str, &nextpos, &op->type);

		op->extended = false;
		if (op->reg > 8) {
			op->extended = true;
			op->reg -= 9;
		}
		if (op->type & OT_REGTYPE & OT_SEGMENTREG) {
			parse_segment_offset(a, str, &nextpos, op, reg_index);
			return nextpos;
		}
		if (op->reg == X86R_UNDEFINED) {
			op->is_good_flag = false;
			if (a->num && a->num->value == 0) {
				return nextpos;
			}
			op->type = OT_CONSTANT;
			RzCore *core = a->num ? (RzCore *)(a->num->userptr) : NULL;
			if (core && rz_flag_get(core->flags, str)) {
				op->is_good_flag = true;
			}

			char *p = strchr(str, '-');
			if (p) {
				op->sign = -1;
				str = ++p;
			}
			op->immediate = getnum(a, str);
		} else if (op->reg < X86R_UNDEFINED) {
			strncpy(op->rep_op, str, MAX_REPOP_LENGTH - 1);
			op->rep_op[MAX_REPOP_LENGTH - 1] = '\0';
		}
	} else { // immediate
		// We don't know the size, so let's just set no size flag.
		op->type = OT_CONSTANT;
		op->sign = 1;
		char *p = strchr(str, '-');
		if (p) {
			op->sign = -1;
			str = ++p;
		}
		op->immediate = getnum(a, str);
	}

	return nextpos;
}

static int parseOpcode(RzAsm *a, const char *op, Opcode *out) {
	out->has_bnd = false;
	bool isrepop = false;
	if (!strncmp(op, "bnd ", 4)) {
		out->has_bnd = true;
		op += 4;
	}
	char *args = strchr(op, ' ');
	out->mnemonic = args ? rz_str_ndup(op, args - op) : rz_str_dup(op);
	out->operands[0].type = out->operands[1].type = 0;
	out->operands[0].extended = out->operands[1].extended = false;
	out->operands[0].reg = out->operands[0].regs[0] = out->operands[0].regs[1] = X86R_UNDEFINED;
	out->operands[1].reg = out->operands[1].regs[0] = out->operands[1].regs[1] = X86R_UNDEFINED;
	out->operands[0].immediate = out->operands[1].immediate = 0;
	out->operands[0].sign = out->operands[1].sign = 1;
	out->operands[0].is_good_flag = out->operands[1].is_good_flag = true;
	out->is_short = false;
	out->operands_count = 0;
	if (args) {
		args++;
	} else {
		return 1;
	}
	if (!rz_str_ncasecmp(args, "short", 5)) {
		out->is_short = true;
		args += 5;
	}
	if (!strncmp(out->mnemonic, "rep", 3)) {
		isrepop = true;
	}
	parseOperand(a, args, &(out->operands[0]), isrepop);
	out->operands_count = 1;
	while (out->operands_count < MAX_OPERANDS) {
		args = strchr(args, ',');
		if (!args) {
			break;
		}
		args++;
		parseOperand(a, args, &(out->operands[out->operands_count]), isrepop);
		out->operands_count++;
	}
	return 0;
}

static ut64 getnum(RzAsm *a, const char *s) {
	if (!s) {
		return 0;
	}
	if (*s == '$') {
		s++;
	}
	return rz_num_math(a->num, s);
}

static int oprep(RzAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	LookupTable *lt_ptr;
	int retval;
	if (!strcmp(op->mnemonic, "rep") ||
		!strcmp(op->mnemonic, "repe") ||
		!strcmp(op->mnemonic, "repz")) {
		data[l++] = 0xf3;
	} else if (!strcmp(op->mnemonic, "repne") ||
		!strcmp(op->mnemonic, "repnz")) {
		data[l++] = 0xf2;
	}
	Opcode instr = { 0 };
	parseOpcode(a, op->operands[0].rep_op, &instr);

	for (lt_ptr = oplookup; strcmp(lt_ptr->mnemonic, "null"); lt_ptr++) {
		if (!rz_str_casecmp(instr.mnemonic, lt_ptr->mnemonic)) {
			if (lt_ptr->opcode > 0) {
				if (lt_ptr->only_x32 && a->bits == 64) {
					free(instr.mnemonic);
					return -1;
				}
				ut64 opcode = lt_ptr->opcode;
				int i = lt_ptr->size - 1;
				for (; i >= 0; i--) {
					data[i + l] = opcode & 0xff;
					opcode >>= 8;
				}
				free(instr.mnemonic);
				return l + lt_ptr->size;
			} else {
				if (lt_ptr->opdo) {
					data += l;
					if (instr.has_bnd) {
						data[l] = 0xf2;
						data++;
					}
					retval = lt_ptr->opdo(a, data, &instr);
					// if op supports bnd then the first byte will
					// be 0xf2.
					if (instr.has_bnd) {
						retval++;
					}
					free(instr.mnemonic);
					return l + retval;
				}
				break;
			}
		}
	}
	free(instr.mnemonic);
	return -1;
}

static int assemble(RzAsm *a, RzAsmOp *ao, const char *str) {
	ut8 __data[32] = { 0 };
	ut8 *data = __data;
	char op[128];
	LookupTable *lt_ptr;
	int retval = -1;
	Opcode instr = { 0 };

	strncpy(op, str, sizeof(op) - 1);
	op[sizeof(op) - 1] = '\0';
	parseOpcode(a, op, &instr);
	for (lt_ptr = oplookup; strcmp(lt_ptr->mnemonic, "null"); lt_ptr++) {
		if (!rz_str_casecmp(instr.mnemonic, lt_ptr->mnemonic)) {
			if (lt_ptr->opcode > 0) {
				if (!lt_ptr->only_x32 || a->bits != 64) {
					ut64 opcode = lt_ptr->opcode;
					int i = lt_ptr->size - 1;
					for (; i >= 0; i--) {
						data[i] = opcode & 0xff;
						opcode >>= 8;
					}
					retval = lt_ptr->size;
				}
			} else {
				if (lt_ptr->opdo) {
					if (instr.has_bnd) {
						data[0] = 0xf2;
						data++;
					}
					retval = lt_ptr->opdo(a, data, &instr);
					// if op supports bnd then the first byte will
					// be 0xf2.
					if (instr.has_bnd) {
						retval++;
					}
				}
			}
			break;
		}
	}
	if (retval > 0) {
		rz_asm_op_set_buf(ao, __data, retval);
	}
	free(instr.mnemonic);
	return retval;
}

RzAsmPlugin rz_asm_plugin_x86_nz = {
	.name = "x86.nz",
	.desc = "x86 handmade assembler",
	.license = "LGPL3",
	.arch = "x86",
	.bits = 16 | 32 | 64,
	.endian = RZ_SYS_ENDIAN_LITTLE,
	.assemble = &assemble
};

#ifndef RZ_PLUGIN_INCORE
RZ_API RzLibStruct rizin_plugin = {
	.type = RZ_LIB_TYPE_ASM,
	.data = &rz_asm_plugin_x86_nz,
	.version = RZ_VERSION
};
#endif
