-- SPDX-License-Identifier: MIT
-- Copyright (C) 2018-present iced project and contributors

local from_hex = require("iced_test_utils").from_hex
local has_int64 = require("iced_test_utils").has_int64

describe("Instruction", function()
	local Code = require("iced_x86.Code")
	local CodeSize = require("iced_x86.CodeSize")
	local ConditionCode = require("iced_x86.ConditionCode")
	local CpuidFeature = require("iced_x86.CpuidFeature")
	local Decoder = require("iced_x86.Decoder")
	local DecoderOptions = require("iced_x86.DecoderOptions")
	local EncodingKind = require("iced_x86.EncodingKind")
	local FlowControl = require("iced_x86.FlowControl")
	local FpuStackIncrementInfo = require("iced_x86.FpuStackIncrementInfo")
	local Instruction = require("iced_x86.Instruction")
	local MemorySize = require("iced_x86.MemorySize")
	local Mnemonic = require("iced_x86.Mnemonic")
	local MvexRegMemConv = require("iced_x86.MvexRegMemConv")
	local OpAccess = require("iced_x86.OpAccess")
	local OpKind = require("iced_x86.OpKind")
	local Register = require("iced_x86.Register")
	local RflagsBits = require("iced_x86.RflagsBits")
	local RoundingControl = require("iced_x86.RoundingControl")

	it("new", function()
		local instr = Instruction.new()
		assert.equals(Code.INVALID, instr:code())
	end)

	it("ip", function()
		local decoder = Decoder.new(64, from_hex("F390" .. "90"), nil, 0x123456789A)
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()

		assert.equals(0x789A, instr1:ip16())
		assert.equals(0x3456789A, instr1:ip32())
		assert.equals(0x123456789A, instr1:ip())
		assert.equals(0x789C, instr1:next_ip16())
		assert.equals(0x3456789C, instr1:next_ip32())
		assert.equals(0x123456789C, instr1:next_ip())
		assert.equals(0x789C, instr2:ip16())
		assert.equals(0x3456789C, instr2:ip32())
		assert.equals(0x123456789C, instr2:ip())

		instr1:set_ip16(0xABCD)
		assert.equals(0xABCD, instr1:ip())
		instr1:set_ip16(-0x8000)
		assert.equals(0x8000, instr1:ip())
		instr1:set_ip16(0xFFFF)
		assert.equals(0xFFFF, instr1:ip())
		assert.has_error(function()
			instr1:set_ip16(-0x8001)
		end)
		assert.has_error(function()
			instr1:set_ip16(0x10000)
		end)

		instr1:set_ip32(0xABCD0123)
		assert.equals(0xABCD0123, instr1:ip())
		instr1:set_ip32(-0x80000000)
		assert.equals(0x80000000, instr1:ip())
		instr1:set_ip32(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr1:ip())
		assert.has_error(function()
			instr1:set_ip32(-0x80000001)
		end)
		assert.has_error(function()
			instr1:set_ip32(0x100000000)
		end)

		instr1:set_ip(0xABCD01235A)
		assert.equals(0xABCD01235A, instr1:ip())

		instr1:set_next_ip16(0xABCD)
		assert.equals(0xABCD, instr1:next_ip())
		instr1:set_next_ip16(-0x8000)
		assert.equals(0x8000, instr1:next_ip())
		instr1:set_next_ip16(0xFFFF)
		assert.equals(0xFFFF, instr1:next_ip())
		assert.has_error(function()
			instr1:set_next_ip16(-0x8001)
		end)
		assert.has_error(function()
			instr1:set_next_ip16(0x10000)
		end)

		instr1:set_next_ip32(0xABCD0123)
		assert.equals(0xABCD0123, instr1:next_ip())
		instr1:set_next_ip32(-0x80000000)
		assert.equals(0x80000000, instr1:next_ip())
		instr1:set_next_ip32(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr1:next_ip())
		assert.has_error(function()
			instr1:set_next_ip32(-0x80000001)
		end)
		assert.has_error(function()
			instr1:set_next_ip32(0x100000000)
		end)

		instr1:set_next_ip(0xABCD01235A)
		assert.equals(0xABCD01235A, instr1:next_ip())

		if has_int64 then
			instr1:set_ip(0xFEDCBA987654321F)
			assert.equals(0xFEDCBA987654321F, instr1:ip())
		end
	end)

	it("code mnemonic code_size len", function()
		local decoder = Decoder.new(64, from_hex("F390" .. "90"), nil, 0x123456789A)
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()

		assert.equals(CodeSize.Code64, instr1:code_size())
		assert.equals(Code.Pause, instr1:code())
		assert.equals(Code.Nopd, instr2:code())
		assert.equals(Mnemonic.Pause, instr1:mnemonic())
		assert.equals(Mnemonic.Nop, instr2:mnemonic())
		assert.equals(2, instr1:len())
		assert.equals(1, instr2:len())
		assert.equals(2, #instr1)
		assert.equals(1, #instr2)

		instr1:set_code(Code.Add_AL_imm8)
		assert.equals(Code.Add_AL_imm8, instr1:code())
		assert.equals(Mnemonic.Add, instr1:mnemonic())
		assert.has_error(function()
			instr1:set_code(0x789A)
		end)

		instr1:set_code_size(CodeSize.Code32)
		assert.equals(CodeSize.Code32, instr1:code_size())
		assert.has_error(function()
			instr1:set_code_size(0x789A)
		end)

		instr1:set_len(12)
		assert.equals(12, instr1:len())
		assert.equals(12, #instr1)
	end)

	it("eq eq_all_bits", function()
		local decoder1 = Decoder.new(64, from_hex("F390" .. "26F390"), nil, 0x123456789A)
		local decoder2 = Decoder.new(64, from_hex("F390"), nil, 0x123456789A)
		local instr1 = decoder1:decode()
		local instr2 = decoder2:decode()
		local instr3 = decoder1:decode()

		assert.is_true(instr1 == instr2)
		assert.is_true(instr2 == instr1)
		assert.is_true(instr1:eq_all_bits(instr2))
		assert.is_true(instr2:eq_all_bits(instr1))
		assert.is_false(instr1 == instr3)
		assert.is_false(instr1:eq_all_bits(instr3))
		assert.is_false(instr3:eq_all_bits(instr1))
	end)

	it("is_invalid", function()
		local decoder = Decoder.new(64, from_hex("F390" .. "F090" .. "00"), nil, 0x123456789A)
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()
		local instr3 = decoder:decode()

		assert.is_false(instr1:is_invalid())
		assert.is_true(instr2:is_invalid())
		assert.is_true(instr3:is_invalid())
	end)

	it("tostring", function()
		local decoder = Decoder.new(64, from_hex("83E55A"), nil, 0x123456789A)
		local instr = decoder:decode()
		assert.equals("and ebp,5Ah", tostring(instr))
	end)

	it("copy", function()
		local decoder = Decoder.new(64, from_hex("F390"), nil, 0x123456789A)
		local instr1 = decoder:decode()
		local instr2 = instr1:copy()
		assert.is_true(instr1 == instr2)
		assert.is_true(instr1:eq_all_bits(instr2))

		-- Make sure they're not the same reference
		instr1:set_code(Code.Adc_AL_imm8)
		assert.equals(Code.Adc_AL_imm8, instr1:code())
		assert.equals(Code.Pause, instr2:code())
	end)

	it("op_count", function()
		local decoder = Decoder.new(64, from_hex("90" .. "50" .. "83E55A" .. "69CE5AA51234"))
		local instr0 = decoder:decode()
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()
		local instr3 = decoder:decode()

		assert.equals(0, instr0:op_count())
		assert.equals(1, instr1:op_count())
		assert.equals(2, instr2:op_count())
		assert.equals(3, instr3:op_count())
	end)

	it("prefixes", function()
		local decoder = Decoder.new(64, from_hex("0118"))
		local instr = decoder:decode()

		assert.is_false(instr:has_xacquire_prefix())
		assert.is_false(instr:has_xrelease_prefix())
		assert.is_false(instr:has_rep_prefix())
		assert.is_false(instr:has_repe_prefix())
		assert.is_false(instr:has_repne_prefix())
		assert.is_false(instr:has_lock_prefix())

		instr:set_has_rep_prefix(true)
		assert.is_true(instr:has_rep_prefix())
		assert.is_true(instr:has_repe_prefix())
		instr:set_has_rep_prefix(false)

		instr:set_has_repe_prefix(true)
		assert.is_true(instr:has_rep_prefix())
		assert.is_true(instr:has_repe_prefix())
		instr:set_has_repe_prefix(false)

		instr:set_has_repne_prefix(true)
		assert.is_true(instr:has_repne_prefix())
		instr:set_has_repne_prefix(false)

		instr:set_has_lock_prefix(true)
		assert.is_true(instr:has_lock_prefix())

		instr:set_has_xacquire_prefix(true)
		assert.is_true(instr:has_xacquire_prefix())
		instr:set_has_xacquire_prefix(false)

		instr:set_has_xrelease_prefix(true)
		assert.is_true(instr:has_xrelease_prefix())
	end)

	it("op_kind", function()
		local instr = Instruction.new()

		instr:set_op0_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op0_kind())
		assert.equals(OpKind.Immediate64, instr:op_kind(0))

		instr:set_op1_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op1_kind())
		assert.equals(OpKind.Immediate64, instr:op_kind(1))

		instr:set_op2_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op2_kind())
		assert.equals(OpKind.Immediate64, instr:op_kind(2))

		instr:set_op3_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op3_kind())
		assert.equals(OpKind.Immediate64, instr:op_kind(3))

		instr:set_op4_kind(OpKind.Immediate8)
		assert.equals(OpKind.Immediate8, instr:op4_kind())
		assert.equals(OpKind.Immediate8, instr:op_kind(4))

		instr:set_op_kind(0, OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op0_kind())
		assert.equals(OpKind.Immediate32, instr:op_kind(0))

		instr:set_op_kind(1, OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op1_kind())
		assert.equals(OpKind.Immediate32, instr:op_kind(1))

		instr:set_op_kind(2, OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op2_kind())
		assert.equals(OpKind.Immediate32, instr:op_kind(2))

		instr:set_op_kind(3, OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op3_kind())
		assert.equals(OpKind.Immediate32, instr:op_kind(3))

		instr:set_op_kind(4, OpKind.Immediate8)
		assert.equals(OpKind.Immediate8, instr:op4_kind())
		assert.equals(OpKind.Immediate8, instr:op_kind(4))

		assert.has_error(function()
			instr:set_op0_kind(0x789A)
		end)
		assert.has_error(function()
			instr:set_op1_kind(0x789A)
		end)
		assert.has_error(function()
			instr:set_op2_kind(0x789A)
		end)
		assert.has_error(function()
			instr:set_op3_kind(0x789A)
		end)
		assert.has_error(function()
			instr:set_op4_kind(0x789A)
		end)
		assert.has_error(function()
			instr:set_op_kind(0, 0x789A)
		end)
		assert.has_error(function()
			instr:set_op_kind(-1, OpKind.Register)
		end)
		assert.has_error(function()
			instr:set_op_kind(1000, OpKind.Register)
		end)
	end)

	it("segment prefix", function()
		local decoder = Decoder.new(64, from_hex("6462F17C8B105001"))
		local instr = decoder:decode()

		assert.is_true(instr:has_segment_prefix())
		assert.equals(Register.FS, instr:segment_prefix())
		assert.equals(Register.FS, instr:memory_segment())

		instr:set_segment_prefix(Register.ES)
		assert.is_true(instr:has_segment_prefix())
		assert.equals(Register.ES, instr:segment_prefix())
		assert.equals(Register.ES, instr:memory_segment())

		instr:set_segment_prefix(Register.None)
		assert.is_false(instr:has_segment_prefix())
		assert.equals(Register.None, instr:segment_prefix())
		assert.equals(Register.DS, instr:memory_segment())
	end)

	it("memory", function()
		local decoder = Decoder.new(64, from_hex("62F27D09A0B48334125AA5"))
		local instr = decoder:decode()

		assert.equals(Code.EVEX_Vpscatterdd_vm32x_k1_xmm, instr:code())

		assert.equals(MemorySize.Int32, instr:memory_size())
		assert.equals(8, instr:memory_displ_size())
		assert.equals(4, instr:memory_index_scale())
		assert.equals(-0x5AA5EDCC, instr:memory_displacement())
		assert.equals(Register.RBX, instr:memory_base())
		assert.equals(Register.XMM0, instr:memory_index())

		instr:set_memory_displ_size(1)
		assert.equals(1, instr:memory_displ_size())
		instr:set_memory_index_scale(8)
		assert.equals(8, instr:memory_index_scale())
		instr:set_memory_displacement(0x12345678)
		assert.equals(0x12345678, instr:memory_displacement())
		instr:set_memory_displacement(-0x12345678)
		assert.equals(-0x12345678, instr:memory_displacement())
		instr:set_memory_base(Register.R14)
		assert.equals(Register.R14, instr:memory_base())
		instr:set_memory_index(Register.XMM15)
		assert.equals(Register.XMM15, instr:memory_index())

		if has_int64 then
			instr:set_memory_displacement(0x123456789ABCDEF1)
			assert.equals(0x123456789ABCDEF1, instr:memory_displacement())
			instr:set_memory_displacement(-0x123456789ABCDEF1)
			assert.equals(-0x123456789ABCDEF1, instr:memory_displacement())
		end

		assert.has_error(function()
			instr:set_memory_base(0x789A)
		end)
		assert.has_error(function()
			instr:set_memory_index(0x789A)
		end)
	end)

	it("ip rel mem", function()
		local decoder1 = Decoder.new(64, from_hex("62F27D09A0B48334125AA5"))
		local instr1 = decoder1:decode()
		local decoder2 = Decoder.new(64, from_hex("013534125AA5"))
		local instr2 = decoder2:decode()

		assert.equals(Code.EVEX_Vpscatterdd_vm32x_k1_xmm, instr1:code())
		assert.equals(Code.Add_rm32_r32, instr2:code())

		assert.is_false(instr1:is_ip_rel_memory_operand())
		assert.is_true(instr2:is_ip_rel_memory_operand())
		assert.equals(Register.RIP, instr2:memory_base())
		assert.equals(Register.None, instr2:memory_index())
		assert.equals(-0x5AA5EDC6, instr2:ip_rel_memory_address())
	end)

	it("evex/mvex props", function()
		local instr = Instruction.new()

		assert.is_false(instr:is_broadcast())
		instr:set_is_broadcast(true)
		assert.is_true(instr:is_broadcast())
		instr:set_is_broadcast(false)
		assert.is_false(instr:is_broadcast())

		assert.is_false(instr:suppress_all_exceptions())
		instr:set_suppress_all_exceptions(true)
		assert.is_true(instr:suppress_all_exceptions())
		instr:set_suppress_all_exceptions(false)
		assert.is_false(instr:suppress_all_exceptions())

		instr:set_code(Code.MVEX_Vcmppd_kr_k1_zmm_zmmmt_imm8)
		assert.is_false(instr:is_mvex_eviction_hint())
		instr:set_is_mvex_eviction_hint(true)
		assert.is_true(instr:is_mvex_eviction_hint())
		instr:set_is_mvex_eviction_hint(false)
		assert.is_false(instr:is_mvex_eviction_hint())

		assert.equals(MvexRegMemConv.None, instr:mvex_reg_mem_conv())
		instr:set_mvex_reg_mem_conv(MvexRegMemConv.RegSwizzleBbbb)
		assert.equals(MvexRegMemConv.RegSwizzleBbbb, instr:mvex_reg_mem_conv())
		assert.has_error(function()
			instr:set_mvex_reg_mem_conv(0x789A)
		end)

		assert.is_false(instr:has_op_mask())
		assert.equals(Register.None, instr:op_mask())
		instr:set_op_mask(Register.K3)
		assert.is_true(instr:has_op_mask())
		assert.equals(Register.K3, instr:op_mask())
		assert.has_error(function()
			instr:set_op_mask(0x789A)
		end)

		assert.is_false(instr:zeroing_masking())
		assert.is_true(instr:merging_masking())

		instr:set_zeroing_masking(true)
		assert.is_true(instr:zeroing_masking())
		instr:set_zeroing_masking(false)
		assert.is_false(instr:zeroing_masking())

		instr:set_merging_masking(true)
		assert.is_true(instr:merging_masking())
		instr:set_merging_masking(false)
		assert.is_false(instr:merging_masking())

		assert.equals(RoundingControl.None, instr:rounding_control())
		instr:set_rounding_control(RoundingControl.RoundToNearest)
		assert.equals(RoundingControl.RoundToNearest, instr:rounding_control())
		assert.has_error(function()
			instr:set_rounding_control(0x789A)
		end)
	end)

	it("immediate", function()
		local instr = Instruction.new()

		instr:set_op0_kind(OpKind.Immediate8)
		assert.equals(OpKind.Immediate8, instr:op0_kind())
		instr:set_immediate8(-0x80)
		assert.equals(0x80, instr:immediate(0))
		assert.equals(0x80, instr:immediate8())
		instr:set_immediate8(0xFF)
		assert.equals(0xFF, instr:immediate(0))
		assert.equals(0xFF, instr:immediate8())
		assert.has_error(function()
			instr:set_immediate8(-0x81)
		end)
		assert.has_error(function()
			instr:set_immediate8(0x100)
		end)

		instr:set_op1_kind(OpKind.Immediate8to16)
		assert.equals(OpKind.Immediate8to16, instr:op1_kind())
		instr:set_immediate8to16(-0x80)
		assert.equals(-0x80, instr:immediate(1))
		assert.equals(-0x80, instr:immediate8to16())
		instr:set_immediate8to16(0x7F)
		assert.equals(0x7F, instr:immediate(1))
		assert.equals(0x7F, instr:immediate8to16())
		assert.has_error(function()
			instr:set_immediate8to16(-0x8001)
		end)
		assert.has_error(function()
			instr:set_immediate8to16(0x10000)
		end)

		instr:set_op2_kind(OpKind.Immediate8to32)
		assert.equals(OpKind.Immediate8to32, instr:op2_kind())
		instr:set_immediate8to32(-0x80)
		assert.equals(-0x80, instr:immediate(1))
		assert.equals(-0x80, instr:immediate8to32())
		instr:set_immediate8to32(0x7F)
		assert.equals(0x7F, instr:immediate(1))
		assert.equals(0x7F, instr:immediate8to32())
		assert.has_error(function()
			instr:set_immediate8to32(-0x80000001)
		end)
		assert.has_error(function()
			instr:set_immediate8to32(0x100000000)
		end)

		instr:set_op3_kind(OpKind.Immediate8to64)
		assert.equals(OpKind.Immediate8to64, instr:op3_kind())
		instr:set_immediate8to64(-0x80)
		assert.equals(-0x80, instr:immediate(1))
		assert.equals(-0x80, instr:immediate8to64())
		instr:set_immediate8to64(0x7F)
		assert.equals(0x7F, instr:immediate(1))
		assert.equals(0x7F, instr:immediate8to64())

		instr:set_op0_kind(OpKind.Immediate8_2nd)
		assert.equals(OpKind.Immediate8_2nd, instr:op0_kind())
		instr:set_immediate8_2nd(-0x80)
		assert.equals(0x80, instr:immediate(0))
		assert.equals(0x80, instr:immediate8_2nd())
		instr:set_immediate8_2nd(0xFF)
		assert.equals(0xFF, instr:immediate(0))
		assert.equals(0xFF, instr:immediate8_2nd())
		assert.has_error(function()
			instr:set_immediate8_2nd(-0x81)
		end)
		assert.has_error(function()
			instr:set_immediate8_2nd(0x100)
		end)

		instr:set_op1_kind(OpKind.Immediate16)
		assert.equals(OpKind.Immediate16, instr:op1_kind())
		instr:set_immediate16(-0x8000)
		assert.equals(0x8000, instr:immediate(1))
		assert.equals(0x8000, instr:immediate16())
		instr:set_immediate16(0xFFFF)
		assert.equals(0xFFFF, instr:immediate(1))
		assert.equals(0xFFFF, instr:immediate16())
		assert.has_error(function()
			instr:set_immediate16(-0x8001)
		end)
		assert.has_error(function()
			instr:set_immediate16(0x10000)
		end)

		instr:set_op2_kind(OpKind.Immediate32to64)
		assert.equals(OpKind.Immediate32to64, instr:op2_kind())
		instr:set_immediate32to64(-0x80000000)
		assert.equals(-0x80000000, instr:immediate(2))
		assert.equals(-0x80000000, instr:immediate32to64())
		instr:set_immediate32to64(0x7FFFFFFF)
		assert.equals(0x7FFFFFFF, instr:immediate(2))
		assert.equals(0x7FFFFFFF, instr:immediate32to64())

		instr:set_op3_kind(OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op3_kind())
		instr:set_immediate32(-0x80000000)
		assert.equals(0x80000000, instr:immediate(3))
		assert.equals(0x80000000, instr:immediate32())
		instr:set_immediate32(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:immediate(3))
		assert.equals(0xFFFFFFFF, instr:immediate32())
		assert.has_error(function()
			instr:set_immediate32(-0x80000001)
		end)
		assert.has_error(function()
			instr:set_immediate32(0x100000000)
		end)

		instr:set_op0_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op0_kind())
		instr:set_immediate64(-0x80000000)
		assert.equals(-0x80000000, instr:immediate(0))
		assert.equals(-0x80000000, instr:immediate64())
		instr:set_immediate64(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:immediate(0))
		assert.equals(0xFFFFFFFF, instr:immediate64())
		if has_int64 then
			instr:set_immediate64(0x123456789ABCDEF1)
			assert.equals(0x123456789ABCDEF1, instr:immediate(0))
			assert.equals(0x123456789ABCDEF1, instr:immediate64())
		end

		instr:set_op0_kind(OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op0_kind())
		instr:set_immediate_i32(0, -0x80000000)
		assert.equals(0x80000000, instr:immediate(0))
		instr:set_immediate_i32(0, 0x7FFFFFFF)
		assert.equals(0x7FFFFFFF, instr:immediate(0))
		instr:set_immediate_i32(0, 0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:immediate(0))
		assert.has_error(function()
			instr:set_immediate_i32(0, -0x80000001)
		end)
		assert.has_error(function()
			instr:set_immediate_i32(0, 0x100000000)
		end)

		instr:set_op0_kind(OpKind.Immediate32)
		assert.equals(OpKind.Immediate32, instr:op0_kind())
		instr:set_immediate_u32(0, -0x80000000)
		assert.equals(0x80000000, instr:immediate(0))
		instr:set_immediate_u32(0, 0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:immediate(0))
		assert.has_error(function()
			instr:set_immediate_u32(0, -0x80000001)
		end)
		assert.has_error(function()
			instr:set_immediate_u32(0, 0x100000000)
		end)

		instr:set_op0_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op0_kind())
		instr:set_immediate_i64(0, -0x80000000)
		assert.equals(-0x80000000, instr:immediate(0))
		instr:set_immediate_i64(0, 0x7FFFFFFF)
		assert.equals(0x7FFFFFFF, instr:immediate(0))
		if has_int64 then
			instr:set_immediate_i64(0, -0x8000000000000000)
			assert.equals(-0x8000000000000000, instr:immediate(0))
			instr:set_immediate_i64(0, 0xFFFFFFFFFFFFFFFF)
			assert.equals(0xFFFFFFFFFFFFFFFF, instr:immediate(0))
			instr:set_immediate_i64(0, 0xFEDCBA987654321F)
			assert.equals(0xFEDCBA987654321F, instr:immediate(0))
		end

		instr:set_op0_kind(OpKind.Immediate64)
		assert.equals(OpKind.Immediate64, instr:op0_kind())
		instr:set_immediate_u64(0, -0x80000000)
		assert.equals(-0x80000000, instr:immediate(0))
		instr:set_immediate_u64(0, 0x7FFFFFFF)
		assert.equals(0x7FFFFFFF, instr:immediate(0))
		if has_int64 then
			instr:set_immediate_u64(0, -0x8000000000000000)
			assert.equals(-0x8000000000000000, instr:immediate(0))
			instr:set_immediate_u64(0, 0xFFFFFFFFFFFFFFFF)
			assert.equals(0xFFFFFFFFFFFFFFFF, instr:immediate(0))
			instr:set_immediate_u64(0, 0xFEDCBA987654321F)
			assert.equals(0xFEDCBA987654321F, instr:immediate(0))
		end
	end)

	it("register", function()
		local instr = Instruction.new()

		instr:set_op0_kind(OpKind.Register)
		instr:set_op0_register(Register.ECX)
		assert.equals(Register.ECX, instr:op0_register())
		assert.equals(Register.ECX, instr:op_register(0))
		instr:set_op_register(0, Register.RDX)
		assert.equals(Register.RDX, instr:op0_register())
		assert.equals(Register.RDX, instr:op_register(0))

		instr:set_op1_kind(OpKind.Register)
		instr:set_op1_register(Register.BP)
		assert.equals(Register.BP, instr:op1_register())
		assert.equals(Register.BP, instr:op_register(1))
		instr:set_op_register(1, Register.DL)
		assert.equals(Register.DL, instr:op1_register())
		assert.equals(Register.DL, instr:op_register(1))

		instr:set_op2_kind(OpKind.Register)
		instr:set_op2_register(Register.ZMM3)
		assert.equals(Register.ZMM3, instr:op2_register())
		assert.equals(Register.ZMM3, instr:op_register(2))
		instr:set_op_register(2, Register.CR8)
		assert.equals(Register.CR8, instr:op2_register())
		assert.equals(Register.CR8, instr:op_register(2))

		instr:set_op3_kind(OpKind.Register)
		instr:set_op3_register(Register.TMM4)
		assert.equals(Register.TMM4, instr:op3_register())
		assert.equals(Register.TMM4, instr:op_register(3))
		instr:set_op_register(3, Register.DR0)
		assert.equals(Register.DR0, instr:op3_register())
		assert.equals(Register.DR0, instr:op_register(3))

		instr:set_op4_register(Register.None)
		assert.equals(Register.None, instr:op4_register())
		assert.equals(Register.None, instr:op_register(4))
		instr:set_op_register(4, Register.None)
		assert.equals(Register.None, instr:op4_register())
		assert.equals(Register.None, instr:op_register(4))

		assert.has_error(function()
			instr:set_op0_register(0x789A)
		end)
		assert.has_error(function()
			instr:set_op1_register(0x789A)
		end)
		assert.has_error(function()
			instr:set_op2_register(0x789A)
		end)
		assert.has_error(function()
			instr:set_op3_register(0x789A)
		end)
		assert.has_error(function()
			instr:set_op4_register(0x789A)
		end)
		assert.has_error(function()
			instr:set_op_register(0, 0x789A)
		end)
		assert.has_error(function()
			instr:set_op_register(-1, Register.RAX)
		end)
		assert.has_error(function()
			instr:set_op_register(1000, Register.RAX)
		end)
	end)

	it("near branch", function()
		local instr = Instruction.new()

		instr:set_op0_kind(OpKind.NearBranch16)
		instr:set_near_branch16(-0x8000)
		assert.equals(0x8000, instr:near_branch16())
		assert.equals(0x8000, instr:near_branch_target())
		instr:set_near_branch16(0xFFFF)
		assert.equals(0xFFFF, instr:near_branch16())
		assert.equals(0xFFFF, instr:near_branch_target())
		assert.has_error(function()
			instr:set_near_branch16(-0x8001)
		end)
		assert.has_error(function()
			instr:set_near_branch16(0x10000)
		end)

		instr:set_op0_kind(OpKind.NearBranch32)
		instr:set_near_branch32(-0x80000000)
		assert.equals(0x80000000, instr:near_branch32())
		assert.equals(0x80000000, instr:near_branch_target())
		instr:set_near_branch32(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:near_branch32())
		assert.equals(0xFFFFFFFF, instr:near_branch_target())
		assert.has_error(function()
			instr:set_near_branch32(-0x80000001)
		end)
		assert.has_error(function()
			instr:set_near_branch32(0x100000000)
		end)

		instr:set_op0_kind(OpKind.NearBranch64)
		instr:set_near_branch64(-0x123456789A)
		assert.equals(-0x123456789A, instr:near_branch64())
		assert.equals(-0x123456789A, instr:near_branch_target())
		instr:set_near_branch64(0x123456789A)
		assert.equals(0x123456789A, instr:near_branch64())
		assert.equals(0x123456789A, instr:near_branch_target())
		if has_int64 then
			instr:set_near_branch64(0xFEDCBA987654321F)
			assert.equals(0xFEDCBA987654321F, instr:near_branch64())
		end
	end)

	it("far branch", function()
		local instr = Instruction.new()

		instr:set_op0_kind(OpKind.FarBranch16)
		instr:set_far_branch_selector(-0x8000)
		instr:set_far_branch16(0xFFFF)
		assert.equals(0x8000, instr:far_branch_selector())
		assert.equals(0xFFFF, instr:far_branch16())
		instr:set_far_branch_selector(0xFFFF)
		instr:set_far_branch16(-0x8000)
		assert.equals(0xFFFF, instr:far_branch_selector())
		assert.equals(0x8000, instr:far_branch16())
		assert.has_error(function()
			instr:set_far_branch_selector(-0x8001)
		end)
		assert.has_error(function()
			instr:set_far_branch_selector(0x10000)
		end)
		assert.has_error(function()
			instr:set_far_branch16(-0x8001)
		end)
		assert.has_error(function()
			instr:set_far_branch16(0x10000)
		end)

		instr:set_op0_kind(OpKind.FarBranch32)
		instr:set_far_branch32(0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:far_branch32())
		instr:set_far_branch32(-0x80000000)
		assert.equals(0x80000000, instr:far_branch32())
		assert.has_error(function()
			instr:set_far_branch32(-0x80000001)
		end)
		assert.has_error(function()
			instr:set_far_branch32(0x100000000)
		end)
	end)

	it("vsib", function()
		local decoder = Decoder.new(64, from_hex("90" .. "C4E2499054A101" .. "C4E2499154A101"))
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()
		local instr3 = decoder:decode()

		assert.is_false(instr1:is_vsib())
		assert.is_false(instr1:is_vsib32())
		assert.is_false(instr1:is_vsib64())
		assert.is_nil(instr1:vsib())

		assert.is_true(instr2:is_vsib())
		assert.is_true(instr2:is_vsib32())
		assert.is_false(instr2:is_vsib64())
		assert.is_false(instr2:vsib())

		assert.is_true(instr3:is_vsib())
		assert.is_false(instr3:is_vsib32())
		assert.is_true(instr3:is_vsib64())
		assert.is_true(instr3:vsib())
	end)

	it("fpu info", function()
		local decoder = Decoder.new(64, from_hex("90" .. "DA18"))
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()

		local info1 = instr1:fpu_stack_increment_info()
		assert.equals(0, info1:increment())
		assert.is_false(info1:conditional())
		assert.is_false(info1:writes_top())

		local info2 = instr2:fpu_stack_increment_info()
		assert.equals(1, info2:increment())
		assert.is_false(info2:conditional())
		assert.is_true(info2:writes_top())

		local fpui1 = FpuStackIncrementInfo.new(0, false, false)
		assert.is_true(info1 == fpui1)
		assert.is_true(fpui1 == info1)

		local fpui2 = FpuStackIncrementInfo.new(1, false, true)
		assert.is_true(info2 == fpui2)
		assert.is_true(fpui2 == info2)

		assert.is_false(info1 == info2)
	end)

	it("sp inc", function()
		local decoder = Decoder.new(64, from_hex("90" .. "50" .. "59"))
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()
		local instr3 = decoder:decode()

		assert.is_false(instr1:is_stack_instruction())
		assert.equals(0, instr1:stack_pointer_increment())

		assert.is_true(instr2:is_stack_instruction())
		assert.equals(-8, instr2:stack_pointer_increment())

		assert.is_true(instr3:is_stack_instruction())
		assert.equals(8, instr3:stack_pointer_increment())
	end)

	it("encoding", function()
		local hex = "50"
			.. "0F0F CD 0C"
			.. "C4E249 50 10"
			.. "8FE848 85 10 40"
			.. "62 F54C0B 58 D3"
			.. "62 F2790B C6 74 A1 01"
		local decoder = Decoder.new(64, from_hex(hex), DecoderOptions.KNC)
		local instr

		instr = decoder:decode()
		assert.equals(EncodingKind.Legacy, instr:encoding())
		instr = decoder:decode()
		assert.equals(EncodingKind.D3NOW, instr:encoding())
		instr = decoder:decode()
		assert.equals(EncodingKind.VEX, instr:encoding())
		instr = decoder:decode()
		assert.equals(EncodingKind.XOP, instr:encoding())
		instr = decoder:decode()
		assert.equals(EncodingKind.EVEX, instr:encoding())
		instr = decoder:decode()
		assert.equals(EncodingKind.MVEX, instr:encoding())
	end)

	it("cpuid", function()
		local decoder = Decoder.new(64, from_hex("62 F17C08 10 D3" .. "85 CE"))

		local instr1 = decoder:decode()
		local cpuid1 = instr1:cpuid_features()
		assert.same({ CpuidFeature.AVX512VL, CpuidFeature.AVX512F }, cpuid1)

		local instr2 = decoder:decode()
		local cpuid2 = instr2:cpuid_features()
		assert.same({ CpuidFeature.INTEL386 }, cpuid2)
	end)

	it("cflow", function()
		local decoder = Decoder.new(64, from_hex("90" .. "CC"))
		local instr1 = decoder:decode()
		local instr2 = decoder:decode()

		assert.equals(FlowControl.Next, instr1:flow_control())
		assert.equals(FlowControl.Interrupt, instr2:flow_control())
	end)

	it("instr props", function()
		local decoder = Decoder.new(64, from_hex("90" .. "E4 5A" .. "0F37" .. "A4"))
		local instr

		instr = decoder:decode()
		assert.is_false(instr:is_privileged())
		assert.is_false(instr:is_save_restore_instruction())
		assert.is_false(instr:is_string_instruction())

		instr = decoder:decode()
		assert.is_true(instr:is_privileged())
		assert.is_false(instr:is_save_restore_instruction())
		assert.is_false(instr:is_string_instruction())

		instr = decoder:decode()
		assert.is_false(instr:is_privileged())
		assert.is_true(instr:is_save_restore_instruction())
		assert.is_false(instr:is_string_instruction())

		instr = decoder:decode()
		assert.is_false(instr:is_privileged())
		assert.is_false(instr:is_save_restore_instruction())
		assert.is_true(instr:is_string_instruction())
	end)

	it("rflags", function()
		local decoder = Decoder.new(64, from_hex("48 09 18" .. "F5" .. "F9" .. "90"))
		local instr

		instr = decoder:decode()
		assert.equals(RflagsBits.None, instr:rflags_read())
		assert.equals(RflagsBits.SF + RflagsBits.ZF + RflagsBits.PF, instr:rflags_written())
		assert.equals(RflagsBits.OF + RflagsBits.CF, instr:rflags_cleared())
		assert.equals(RflagsBits.None, instr:rflags_set())
		assert.equals(RflagsBits.AF, instr:rflags_undefined())
		assert.equals(
			RflagsBits.SF + RflagsBits.ZF + RflagsBits.PF + RflagsBits.OF + RflagsBits.CF + RflagsBits.AF,
			instr:rflags_modified()
		)

		instr = decoder:decode()
		assert.equals(RflagsBits.CF, instr:rflags_read())
		assert.equals(RflagsBits.CF, instr:rflags_written())
		assert.equals(RflagsBits.None, instr:rflags_cleared())
		assert.equals(RflagsBits.None, instr:rflags_set())
		assert.equals(RflagsBits.None, instr:rflags_undefined())
		assert.equals(RflagsBits.CF, instr:rflags_modified())

		instr = decoder:decode()
		assert.equals(RflagsBits.None, instr:rflags_read())
		assert.equals(RflagsBits.None, instr:rflags_written())
		assert.equals(RflagsBits.None, instr:rflags_cleared())
		assert.equals(RflagsBits.CF, instr:rflags_set())
		assert.equals(RflagsBits.None, instr:rflags_undefined())
		assert.equals(RflagsBits.CF, instr:rflags_modified())

		instr = decoder:decode()
		assert.equals(RflagsBits.None, instr:rflags_read())
		assert.equals(RflagsBits.None, instr:rflags_written())
		assert.equals(RflagsBits.None, instr:rflags_cleared())
		assert.equals(RflagsBits.None, instr:rflags_set())
		assert.equals(RflagsBits.None, instr:rflags_undefined())
		assert.equals(RflagsBits.None, instr:rflags_modified())
	end)

	it("branches", function()
		local hex
		local decoder
		local instr

		hex = "90"
			.. "74 5A"
			.. "0F84 5AA51234"
			.. "EB 5A"
			.. "E9 12345AA5"
			.. "E8 12345AA5"
			.. "FF E6"
			.. "FF D1"
			.. "C4E078 74 5A"
			.. "C5F8 85 5AA51234"
			.. "E3 5A"
			.. "E2 5A"
			.. "E1 5A"
			.. "E0 5A"
		decoder = Decoder.new(64, from_hex(hex), DecoderOptions.KNC)

		-- Nopd
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Je_rel8_64
		instr = decoder:decode()
		assert.is_true(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_true(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Je_rel32_64
		instr = decoder:decode()
		assert.is_true(instr:is_jcc_short_or_near())
		assert.is_true(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jmp_rel8_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_true(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_true(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jmp_rel32_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_true(instr:is_jmp_near())
		assert.is_true(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Call_rel32_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_true(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jmp_rm64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_true(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Call_rm64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_true(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- VEX_KNC_Jkzd_kr_rel8_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_true(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_true(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- VEX_KNC_Jknzd_kr_rel32_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_true(instr:is_jkcc_short_or_near())
		assert.is_true(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jrcxz_rel8_64
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_true(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Loop_rel8_64_RCX
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_true(instr:is_loop())

		-- Loope_rel8_64_RCX
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_true(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Loopne_rel8_64_RCX
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_true(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Far branches

		hex = "9A 12345678 9ABC" .. "EA 12345678 EABC" .. "FF 18" .. "FF 28"
		decoder = Decoder.new(32, from_hex(hex))

		-- Call_ptr1632
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_true(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jmp_ptr1632
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_true(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Call_m1632
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_false(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_true(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())

		-- Jmp_m1632
		instr = decoder:decode()
		assert.is_false(instr:is_jcc_short_or_near())
		assert.is_false(instr:is_jcc_near())
		assert.is_false(instr:is_jcc_short())
		assert.is_false(instr:is_jmp_short())
		assert.is_false(instr:is_jmp_near())
		assert.is_false(instr:is_jmp_short_or_near())
		assert.is_false(instr:is_jmp_far())
		assert.is_false(instr:is_call_near())
		assert.is_false(instr:is_call_far())
		assert.is_false(instr:is_jmp_near_indirect())
		assert.is_true(instr:is_jmp_far_indirect())
		assert.is_false(instr:is_call_near_indirect())
		assert.is_false(instr:is_call_far_indirect())
		assert.is_false(instr:is_jkcc_short_or_near())
		assert.is_false(instr:is_jkcc_near())
		assert.is_false(instr:is_jkcc_short())
		assert.is_false(instr:is_jcx_short())
		assert.is_false(instr:is_loopcc())
		assert.is_false(instr:is_loop())
	end)

	it("cc branches", function()
		local decoder = Decoder.new(64, from_hex("74 5A" .. "90"))
		local instr

		instr = decoder:decode()
		assert.equals(ConditionCode.e, instr:condition_code())
		assert.equals(Code.Je_rel8_64, instr:code())
		instr:as_near_branch()
		assert.equals(Code.Je_rel32_64, instr:code())
		instr:as_short_branch()
		assert.equals(Code.Je_rel8_64, instr:code())
		instr:negate_condition_code()
		assert.equals(Code.Jne_rel8_64, instr:code())

		instr = decoder:decode()
		assert.equals(ConditionCode.None, instr:condition_code())
		assert.equals(Code.Nopd, instr:code())
		instr:as_near_branch()
		assert.equals(Code.Nopd, instr:code())
		instr:as_short_branch()
		assert.equals(Code.Nopd, instr:code())
		instr:negate_condition_code()
		assert.equals(Code.Nopd, instr:code())
	end)

	it("db", function()
		local instr = Instruction.new()
		instr:set_code(Code.DeclareByte)

		assert.equals(1, instr:declare_data_len())
		instr:set_declare_data_len(16)
		assert.equals(16, instr:declare_data_len())

		instr:set_declare_byte_value(0, -0x80)
		assert.equals(0x80, instr:get_declare_byte_value(0))
		assert.equals(-0x80, instr:get_declare_byte_value_i8(0))
		instr:set_declare_byte_value(15, 0xFF)
		assert.equals(0xFF, instr:get_declare_byte_value(15))
		assert.equals(-1, instr:get_declare_byte_value_i8(15))

		assert.has_error(function()
			instr:set_declare_byte_value(0, -0x81)
		end)
		assert.has_error(function()
			instr:set_declare_byte_value(0, 0x100)
		end)
		assert.has_error(function()
			instr:get_declare_byte_value(16)
		end)
		assert.has_error(function()
			instr:set_declare_byte_value(16, 0)
		end)
	end)

	it("dw", function()
		local instr = Instruction.new()
		instr:set_code(Code.DeclareWord)

		assert.equals(1, instr:declare_data_len())
		instr:set_declare_data_len(8)
		assert.equals(8, instr:declare_data_len())

		instr:set_declare_word_value(0, -0x8000)
		assert.equals(0x8000, instr:get_declare_word_value(0))
		assert.equals(-0x8000, instr:get_declare_word_value_i16(0))
		instr:set_declare_word_value(7, 0xFFFF)
		assert.equals(0xFFFF, instr:get_declare_word_value(7))
		assert.equals(-1, instr:get_declare_word_value_i16(7))

		assert.has_error(function()
			instr:set_declare_word_value(0, -0x8001)
		end)
		assert.has_error(function()
			instr:set_declare_word_value(1, 0x10000)
		end)
		assert.has_error(function()
			instr:get_declare_word_value(8)
		end)
		assert.has_error(function()
			instr:set_declare_word_value(8, 0)
		end)
	end)

	it("dd", function()
		local instr = Instruction.new()
		instr:set_code(Code.DeclareDword)

		assert.equals(1, instr:declare_data_len())
		instr:set_declare_data_len(4)
		assert.equals(4, instr:declare_data_len())

		instr:set_declare_dword_value(0, -0x80000000)
		assert.equals(0x80000000, instr:get_declare_dword_value(0))
		assert.equals(-0x80000000, instr:get_declare_dword_value_i32(0))
		instr:set_declare_dword_value(3, 0xFFFFFFFF)
		assert.equals(0xFFFFFFFF, instr:get_declare_dword_value(3))
		assert.equals(-1, instr:get_declare_dword_value_i32(3))

		assert.has_error(function()
			instr:set_declare_dword_value(0, -0x80000001)
		end)
		assert.has_error(function()
			instr:set_declare_dword_value(1, 0x100000000)
		end)
		assert.has_error(function()
			instr:get_declare_dword_value(4)
		end)
		assert.has_error(function()
			instr:set_declare_dword_value(4, 0)
		end)
	end)

	it("dq", function()
		local instr = Instruction.new()
		instr:set_code(Code.DeclareQword)

		assert.equals(1, instr:declare_data_len())
		instr:set_declare_data_len(2)
		assert.equals(2, instr:declare_data_len())

		instr:set_declare_qword_value(0, 0xABCD01235A)
		assert.equals(0xABCD01235A, instr:get_declare_qword_value(0))
		assert.equals(0xABCD01235A, instr:get_declare_qword_value_i64(0))
		instr:set_declare_qword_value(1, -0xABCD01235A)
		assert.equals(-0xABCD01235A, instr:get_declare_qword_value(1))
		assert.equals(-0xABCD01235A, instr:get_declare_qword_value_i64(1))

		if has_int64 then
			instr:set_declare_qword_value(0, 0xFEDCBA987654321F)
			assert.equals(0xFEDCBA987654321F, instr:get_declare_qword_value(0))
			assert.equals(0xFEDCBA987654321F, instr:get_declare_qword_value_i64(0))
		end

		assert.has_error(function()
			instr:get_declare_qword_value(2)
		end)
		assert.has_error(function()
			instr:set_declare_qword_value(2, 0)
		end)
	end)

	it("OpCodeInfo", function()
		local instr = Instruction.new()
		instr:set_code(Code.Adc_AL_imm8)

		local op_code = instr:op_code()
		assert.equals(Code.Adc_AL_imm8, op_code:code())
	end)

	it("instr info", function()
		local decoder = Decoder.new(64, from_hex("C4E349 48 10 41"))
		local instr = decoder:decode()

		local used_registers1, is_nil1 = instr:used_registers()
		assert.is_nil(is_nil1)
		local used_memory1, is_nil2 = instr:used_memory()
		assert.is_nil(is_nil2)
		local op_accesses1, is_nil3 = instr:op_accesses()
		assert.is_nil(is_nil3)
		local used_registers2, used_memory2, is_nil4 = instr:used_regs_mem()
		assert.is_nil(is_nil4)
		local used_registers3, used_memory3, op_accesses3, is_nil5 = instr:used_values()
		assert.is_nil(is_nil5)

		assert.same(used_registers1, used_registers2)
		assert.same(used_registers1, used_registers3)
		assert.same(used_memory1, used_memory2)
		assert.same(used_memory1, used_memory3)
		assert.same(op_accesses1, op_accesses3)

		assert.same({ OpAccess.Write, OpAccess.Read, OpAccess.Read, OpAccess.Read, OpAccess.Read }, op_accesses1)

		assert.equals(1, #used_memory1)
		local mem = used_memory1[1]
		assert.equals(Register.DS, mem:segment())
		assert.equals(Register.RAX, mem:base())
		assert.equals(Register.None, mem:index())
		assert.equals(1, mem:scale())
		assert.equals(0, mem:displacement())
		assert.equals(MemorySize.Packed128_Float32, mem:memory_size())
		assert.equals(OpAccess.Read, mem:access())
		assert.equals(CodeSize.Code64, mem:address_size())
		assert.equals(0, mem:vsib_size())

		assert.equals(4, #used_registers1)
		assert.same({ Register.ZMM2, OpAccess.Write }, { used_registers1[1]:register(), used_registers1[1]:access() })
		assert.same({ Register.XMM6, OpAccess.Read }, { used_registers1[2]:register(), used_registers1[2]:access() })
		assert.same({ Register.RAX, OpAccess.Read }, { used_registers1[3]:register(), used_registers1[3]:access() })
		assert.same({ Register.XMM4, OpAccess.Read }, { used_registers1[4]:register(), used_registers1[4]:access() })

		local mem2 = mem:copy()
		assert.is_true(mem == mem2)
		assert.is_true(mem2 == mem)

		local reg1 = used_registers1[1]
		local reg2 = reg1:copy()
		local reg3 = used_registers1[2]
		assert.is_true(reg1 == reg2)
		assert.is_true(reg2 == reg1)
		assert.is_false(reg1 == reg3)
	end)
end)
