Here are the single byte x86 opcodes. This is literally a “byte-code” for the x86. To be more precise, these should work on any x86 processor. I will note any instructions that require specific later models.
Some instructions may have an opcode and operands… but they will still fit into one byte. These typically encode a register value into a part of the byte.
After building this list, I started a new list to include instructions that are a total of two bytes. These may be two byte opcodes or an opcode and a single byte operand.
Lastly, it is interesting to note that of all the common operations, only INC and DEC affect the status register as a side effect. Almost all of the common operations are MOV operations and thus do not affect status.
Instruction / Mnemonic | Description | Opcode | Notes |
---|---|---|---|
Stack Operations | |||
PUSHA | Push All | 0b0110000 0x60 | Temp <- ESP; PUSH EAX, ECX, EDX, EBX, Temp, EBP, ESI, EDI |
PUSH reg | PUSH reg to stack | 0b01010rrr 0x50 + r | ESP <- ESP – 4; (SS:ESP) <- REG |
POPA | Pop All | 0b0110001 0x61 | POPS EDI, ESI, EBP, *ESP, EBX, EDX, ECX, EAX (* not used) |
POP reg | POP stack to reg | 0b01011rrr 0x58 + r | REG <- (SS:ESP); ESP <- ESP + 4; |
Common Register / Integer Operations | |||
CBW/CWDE | Convert Byte to Word/Convert Word to Double Word | 0b10011000 0x98 | If 16 bit operand mode, AX = SignExtend(AL). If 32 bit operand mode, EAX = SignExtend(AX). NOTE: the similarities and differences with CWD/CDQ ! |
CWD/CDQ | Convert Word to Doubleword/Convert Doubleword to Quadword | 0b10011001 0x99/td> | If 16 bit operand mode: if AX < 0 then DX = 0xFFFFH else DX = 0; If 32 bit operand mode: if EAX < 0 then EDX = 0xFFFFFFFFH else EDX = 0. NOTE: contrast with above CBW/CWDE ! This definitely was the legacy 8086 way of doing 32 bit sign extension. |
DEC | DEC reg | 0b01001rrr 0x48 + r |
Does not set carry flag. NOTE: this becomes the REX prefix for x86-64 mode |
INC | INC reg | 0b01000rrr 0x40 + r |
Does not set carry flag. NOTE: this becomes the REX prefix for x86-64 mode |
MOVSB | Move Byte String | 0b10100100 0xA4 | Move byte from DS:[ESI] to ES:[EDI], then +-=1 to ESI and EDI. Segment override only on source. |
MOVSD | Move Double Word String | 0b10100101 0xA5 | Depending on mode. 16 bit increment = 2, 32 bit increment = 4. Move double word from DS:[ESI] to ES:[EDI], then +-=increment to ESI and EDI. Segment override only on source. |
NOP | NOP | 0b10010000 0x90 | Really just XCHG EAX, EAX. This is the Intel approved way. |
XCHG EAX,reg | Swap EAX, reg | 0b10010rrr 0x90 + r | Temp <- EAX; EAX <- r; r <- Temp; (* see NOP above) |
XLAT | 0b11010111 0xD7 | AL = [AL + EBX]; 16 bit operand works too. NOTE: because this is a single byte, there is no way to specify a segment override. Therefore, they allow in assembler an operand so the segment override can be specified. This operand is ignored. | |
Control Flow | |||
Interrupts | |||
HLT | Halt | 11110100 0xF4 | Resumes on any interrupt (or RESET). NOTE THIS IS A PRIVILEGED INSTRUCTION; #GP(0) if the current privilege level is not 0 |
INT3 | Interrupt Type 3 | 11001100 0xCC | Interesting bit pattern. Usually used for debugger break. |
INTO | Interrupt if Overflow | 11001110 0xCE | Interrupt Type 4 if Overflow flag is set. |
IRET | Interrupt Return | 11001111 0xCF | |
LEAVE | High Level Procedure Exit | 11001001 0xC9 | Reverses an enter. ESP <- EBP; POP EBP; |
RET | Near Return | 11000011 0xC3 | Near Return to Caller (No segment pulled off of stack). No stack adjust after return either. For 32 bits, 32 bit address return. |
RET | FAR Return | 11001011 0xCB | Far Return to Caller. Selector on stack. May cause privilege / stack / etc. changes |
Status Code Operations | |||
CMC | Complement Carry Flag | 011110101 0xF5 | Invert carry flag. |
CLC | Clear Carry Flag | 011111000 0xF8 | |
STC | Set Carry Flag | 011111001 0xF9 | Really these instructions use low order bit as future carry bit |
CLI | Clear Interrupt Enable Flag | 011111010 0xFA | |
STI | Set Interrupt Enable Flag | 0b11111011 0xFB | Really these instructions use low order bit as future I bit |
CLD | Clear Direction Flag | 0b11111100 0xFC | All ops using ESI or EDI now increment. |
STD | Set Direction Flag | 0b11111101 0xFD | All ops using ESI or EDI now decrement. |
SAHF | Store AH into Flags | 0b10011110 0x9E | Copy AH to low 8 bits of FLAGS. SF ZF xx AF xx PF xx CF |
LAHF | Load AH with Flags | 0b10011111 0x9F | Copy low 8 bits of FLAGS to AH. SF ZF xx AF xx PF xx CF |
SALC | Load AL with state of Carry | 0b11010110 0xD6 | AL = C ? -1 : 0; or AL = C ? 0xff : 0x00. No flags affected. Previously undocumented instruction. Invalid on x86-64 🙁 |
Opcode Prefixes | |||
A16/A32 | Invert Default Address Size for Following Instruction | 0b01100111 0x67 | |
This is NASM syntax. Typically precedes an operand size prefix. For example, if code segment descriptor ‘D’ bit is set to 1, then segment default address and operand size = 32. Using an A16 prefix will invert the next instruction (not the next prefix) to 16 bit address size. Also note, that typically, the assembler will be told the default mode for code generation (USE16 or USE32), so either the A16 or A32 prefix will not emit an instruction if used in the same segment. | |||
O16/O32 | Invert Default Operand Size for Following Instruction | 0b01100110 0x66 | |
This is NASM syntax. Typically follows an address size prefix. For example, if code segment descriptor ‘D’ bit is set to 1, then segment default address and operand size = 32. Using an O16 prefix will invert the next instruction (not the next prefix) to 16 bit operand size. Also note, that typically, the assembler will be told the default mode for code generation (USE16 or USE32), so either the O16 or O32 prefix will not emit an instruction if used in the same segment. | |||
ES: | Override default segment register for following instruction. | 0b00100110 0x26 | |
CS: | 0b00101110 0x2E | ||
SS: | 0b00110110 0x36 | ||
DS: | 0b00111110 0x3E | ||
FS: | 0b01100100 0x64 | ||
GS: | 0b01100101 0x65 | ||
Note: these can never override the CS for instruction fetch or the SS for implicit stack operations | |||
REP | REPeat following String Instruction until Counter to Zero | 0b11110011 0xF3 | Repeats following string instruction until the automatic decrement of (E)CX is 0. This prefix is *only* valid for INS, MOVS, OUTS, LODS, STOS instructions. Notice the opcode is the same as REPE/REPZ. |
REPE / REPZ | REPeat following String Instruction until Equal to Zero | 0b11110011 0xF3 | Repeats following string instruction until the decrement of (E)CX is 0 or the instruction sets the Zero Flag to 1. This prefix is *only* valid for CMPS and SCAS instructions. Notice the opcode is the same as REP. |
REPNE / REPNZ | REPeat following string instruction until Not Equal to Zero | 0b11110010 0xF2 | Repeats following string instruction until the decrement of (E)CX is 0 or the instruction sets the Zero Flag to 0. This prefix is *only* valid for CMPS and SCAS instructions. |
Instruction / Mnemonic | Description | Opcode | Notes |
---|---|---|---|
Integer Operations | |||
ADD AL,imm8 | 0b00000100 0x04 imm8 | Regardless of mode (16/32), only works on AL and an 8 bit operand. | |
ADC AL,imm8 | Add with Carry | 0b00010100 0x14 imm8 | Regardless of mode (16/32), only works on AL and an 8 bit operand. |
AND AL,imm8 | 0b00100100 0x24 imm8 | Regardless of mode (16/32), only works on AL and an 8 bit operand. | |
XOR AL,imm8 | 0b00110100 0x34 imm8 | Regardless of mode (16/32), only works on AL and an 8 bit operand. | |
XOR EAX,EAX | 0b00110011 0x33 0b11000000 0xc0 | Regardless of mode (16/32). Fastest way to clear EAX. | |
AAD imm8 | ASCII Adjust AX before Division | 0b11010101 0xD5, imm8 | NOTE: DOES MULTIPLICATION. AL := AH * imm8 + AL; AH := 0; Originally for BCD conversion. |
AAM imm8 | ASCII Adjust AX after Multiply | 0b11010100 0xD4, imm8 | NOTE: DOES DIVISION. AH := AL / imm8; AL := AL MOD imm8; Originally for BCD conversion. |
BSWAP reg | Byte order SWAP | 0x0f 0b11001rrr 0xC8+r | Switches endian order of specified register. Note: Undocumented, for 16 bit operands, it just clears the register! |
Register to Register Operations (these are just a particular mode of ModRM byte) | |||
XOR r8,r8 | 0b00110000 0x30 0b11 sss ddd (dest, src reg bits) | Regardless of mode (16/32). Note the order difference with below. Also, uses special 8 bit registers. | |
XOR r16/32,r16/32 | 0b00110001 0x31 0b11 sss ddd (dest, src reg bits) | Depends on mode (16/32), also fastest way to clear any register. | |
XOR r8,r8 | 0b00110011 0x33 0b11 ddd sss (dest, src reg bits) | Regardless of mode (16/32). Note the order difference with above. Also, uses special 8 bit registers. | |
XOR r16/32,r16/32 | 0b00110011 0x33 0b11 ddd sss (dest, src reg bits) | Depends on mode (16/32), also fastest way to clear any register. | |
XCHG r8,r8 | eXCHanGe Register/Memory | 0b10000110 0x86 0b11 aaa bbb (aaa, bbb – reg bits) | Regardless of mode (16/32). Register order doesn’t matter. Also, uses special 8 bit registers. |
XCHG r16/32,r16/32 | eXCHanGe Register/Memory | 0b10000111 0x87 0b11 aaa bbb (aaa, bbb – reg bits) | Depends on mode (16/32). Register order doesn’t matter. |
Control Flow Operations | |||
LOOP | Loop (E)CX times. | 11100010 0xE2 0b???????? (signed 8 bit offset) | Decrement Counter Register and then Branch when counter <> 0 |
LOOPE / LOOPZ | Loop while (E)CX time or while and ZF = 1 | 11100001 0xE1 0b???????? (signed 8 bit offset) | Decrement Counter Register and then Branch when counter <> 0 and ZF = 1 |
LOOPNE / LOOPNZ | Loop while (E)CX time or while and ZF = 0 | 11100000 0xE0 0b???????? (signed 8 bit offset) | Decrement Counter Register and then Branch when counter <> 0 and ZF = 0 |
Interrupt Operations | |||
INT n | Interrupt Type n | 0b11001101 0xCD | 2 BYTES, single byte operand is interrupt #. Note for INT 3, there is a single byte version mentioned above. |
Misc Operations – Pentium and Higher | |||
RDTSC | ReaD TimeStamp Counter | 0x0f 0x31 | 2 byte opcode. Puts 64 bit counter into EDX:EAX. Lots of inconsistencies when virtualization, multiple processors, or power-save are involved. Still very handy. |
CPUID | 0x0f 0xA2 | 2 byte opcode. EAX = Maximum Input Value for Basic CPUID Information (not specified here). EBX = “Genu”, ECX = “ntel”, EDX = “ineI” |
32 bit | 16 bit | 8 bit (Rarely Used) | Number |
---|---|---|---|
EAX | AX | AL | 0 |
ECX | CX | CL | 1 |
EDX | DX | DL | 2 |
EBX | BX | BL | 3 |
ESP | SP | AH | 4 |
EBP | BP | CH | 5 |
ESI | SI | DH | 6 |
EDI | DI | BH | 7 |
Note: by default, in 32 bit mode, the registers used are 32 bit. If the operand override is specified or the segment has a 16 bit operand as the default, then the 16 bit registers are used instead of 32 bit. The 8 bit registers are only used in special instances with separate opcodes. Assume the default for registers is 32 bit. I will call out the specific cases for 8 bit registers above.
Register | Number | Notes |
---|---|---|
EAX | 0 | Accumulator |
ECX | 1 | Counter (Loop counters) |
EDX | 2 | Data (MSB Results from EAX multiply,divide,etc) |
EBX | 3 | Base / General Purpose – Once specialized use with XLAT |
ESP | 4 | Current stack pointer |
EBP | 5 | Previous Stack Frame Link |
ESI | 6 | Source Index Pointer – String/Buffer Operations |
EDI | 7 | Destination Index Pointer – String/Buffer Operations |
Borrowed from Swansontech, see below
Opcode | Bytes | Speed |
---|---|---|
MOV EAX,0 | B8 00 00 00 00 | ok |
XOR EAX,EAX | 33 C0 | FASTER |
Notes
The branchless CMOV instruction requires a two byte opcode. 0x0f 0x4n.
A. Tannenbaum discovered that most programs only require 13 bits for constants.