Single Byte or Small x86 Opcodes

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.

Single Byte Total
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.
Two Byte – 1 Byte Opcode and 1 Byte Operand Instructions or 2 Byte Opcode and 0 Bytes Operand – 2 Bytes Total (Under Construction)
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”
Register Table
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 Uses
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

Zeroing A Register
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.

 

Other Good References
x86-64 System V ABI Register Usage
Intel x86 JUMP quick reference
swansontec – The Art of Picking Intel Registers
x86 oddities

Old Tricks, New Dogs: Removing Branches (archive.org backup)