x86 Instruction Families

Last update: 2022-02-24 03:27:04 +0800 +0800
tags: X86 | X86-64 | Assembly
Available translations: 日本語 / 中文(简体) / 中文(繁體)

x86 groups a large, irregular instruction set behind common data movement, arithmetic, bit manipulation, control-flow, and string-processing patterns. Operand size, address size, execution mode, prefixes, and ISA extensions can change an instruction’s encoding or availability. Intel’s and AMD’s architecture manuals remain authoritative; successful assembly only proves that a tool can encode the source.

Data Movement and Address Generation #

MOV copies an immediate or register value to a register or memory, or copies memory to a register. An ordinary MOV has at most one explicit memory operand. MOVZX zero-extends a narrower integer, while MOVSX and MOVSXD sign-extend. A 32-bit general-purpose-register destination in 64-bit mode implicitly clears the upper 32 bits.

CMOVcc conditionally copies a register or memory source according to status flags without changing control flow. A memory-source conditional move can still perform the source read before selecting the result, so it must not be used to guard an invalid address. SETcc writes a byte result from the same condition families.

XCHG exchanges two operands. Its memory form has locked semantics even without an explicit LOCK prefix, making it atomic with respect to other processors for the addressed memory. Atomicity does not make a compound algorithm correct; ordering, cache-line placement, and retry behavior still matter.

LEA calculates an effective address and writes the arithmetic result without accessing memory:

asm
lea rax, [rdi + rdi*4]    ; rax = rdi * 5

Compilers use LEA for pointer calculation and selected additions or constant multiplications. Segment bases and paging are not applied because no memory operand is dereferenced.

Stack Operations #

PUSH decrements the stack pointer and stores a value; POP loads a value and increments it. Exact operand-size behavior depends on mode. The platform ABI, not the instruction, determines alignment, red zones, shadow space, saved registers, and which memory is valid stack storage.

CALL stores a return address and transfers control, while a near RET loads the return address from the stack. A mismatched stack pointer or corrupted return address therefore changes control flow. Far calls, far returns, call gates, and IRET additionally process segment or privilege state and should not be treated as ordinary function-call instructions.

Integer Arithmetic, Comparison, and Flags #

ADD, ADC, SUB, and SBB implement ordinary and multi-precision arithmetic. ADC consumes carry flag CF, and SBB consumes it as a borrow, so low-to-high limb sequences propagate carry or borrow. INC and DEC update several arithmetic flags but preserve CF.

CMP performs a subtraction only to set flags. TEST performs a bitwise AND only to set flags. Conditional branches, moves, and byte-setting instructions then interpret those flags:

  • Equality uses ZF.
  • Unsigned ordering uses CF and ZF.
  • Signed ordering combines SF, OF, and ZF.

CF records unsigned carry or borrow, while OF records signed overflow. The same bit pattern therefore requires a signed or unsigned condition matching the program’s data interpretation.

Single-operand MUL and IMUL multiply an accumulator by an explicit operand and produce a double-width result in implicit high and low registers. Two- and three-operand IMUL forms write a truncated result to an explicit destination. DIV and IDIV divide a double-width implicit dividend. They raise divide error for a zero divisor or a quotient that cannot fit the destination; software must prepare the high half correctly before division.

Logical, Bit-Test, Shift, and Rotate Operations #

AND, OR, XOR, and NOT operate bitwise. Clearing a register with xor eax, eax is a common dependency-breaking idiom on current out-of-order processors, but its architectural result is simply zero.

BT copies a selected bit to CF; BTS, BTR, and BTC also set, reset, or complement the bit. A memory bit index can address beyond the encoded memory operand, which makes bounds reasoning important. An explicit LOCK prefix can make supported read-modify-write memory forms atomic.

BSF and BSR locate the least- or most-significant set bit. Their destination is undefined for a zero source. TZCNT and LZCNT provide defined zero-input results when the relevant extension is present, but their encodings can execute as BSF or BSR on processors without that extension. Feature detection is therefore required.

SHL and SAL are equivalent left shifts. SHR shifts in zero bits, while SAR replicates the sign bit. ROL and ROR rotate within the operand; RCL and RCR rotate through CF. Shift counts are masked according to operand size, and flag results for large or zero counts require consulting the instruction definition.

Direct and Indirect Control Flow #

Near JMP, CALL, and conditional Jcc instructions can use relative displacements measured from the end of the instruction. Indirect JMP and CALL read the destination from a register or memory. RET obtains its destination from the stack. Indirect targets interact with control-flow integrity, return prediction, and speculation mitigations, so equivalent-looking forms can have different security or performance properties.

Short conditional branches encode an 8-bit signed displacement. Near forms use a wider displacement in 32- and 64-bit modes. x86 has no general instruction that conditionally calls or returns; code composes those operations with a conditional branch.

Exceptions, software interrupts, and external interrupts transfer through the interrupt descriptor table. INT, IRET, and gate-based transfers have privilege semantics beyond ordinary branches. SYSCALL/SYSRET and SYSENTER/SYSEXIT use model-specific configuration and an operating-system ABI.

String Instructions and Repeat Prefixes #

MOVS, CMPS, SCAS, LODS, and STOS use implicit source, destination, accumulator, and count registers. Byte, word, doubleword, and selected quadword forms make element size explicit. The direction flag (DF) chooses whether index registers increment or decrement.

REP repeats an operation while the count is nonzero. REPE/REPZ and REPNE/REPNZ additionally use ZF with comparison or scan instructions. Current processors can optimize large REP MOVSB and REP STOSB operations, but feature and size-dependent library dispatch remains an implementation decision.

Forward rep movsb is unsafe when the destination starts inside and above the source range because early stores overwrite bytes that have not been read. Backward copying can handle that overlap:

asm
std             ; decrement indexes
rep movsb
cld             ; restore the ABI-required direction

The caller must initialize pointers to the final elements for a backward copy. Most x86 ABIs require DF=0 on function entry and return, so code that executes STD must restore it even on failure paths. Production memmove implementations also choose direction and optimized sequences according to overlap, size, and processor features.

Privileged operations such as CLI, STI, LGDT, LIDT, control-register access, port I/O, and HLT are constrained by current mode and privilege. Assembler acceptance does not grant execution permission.

Comments

Read or comment on GitHub (sign-in required to comment)