This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revision | |||
| en:multiasm:papc:chapter_6_6 [2026/02/19 20:42] – [MOD R/M byte] ktokarz | en:multiasm:papc:chapter_6_6 [2026/02/19 20:48] (current) – [Scale Index Base byte] ktokarz | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Principles of Instructions Encoding ====== | ||
| + | |||
| + | In x86 processors, instructions are encoded using varying numbers of bytes. Some instructions are encoded in just a single byte, others require several bytes, and some are as long as 15 bytes. In general, instructions are composed with some fields as presented in figure {{ref> | ||
| + | |||
| + | <figure instr_fields> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | The fields are as follows | ||
| + | * Instruction prefixes | ||
| + | * Opcode | ||
| + | * MODR/M byte | ||
| + | * SIB byte (Scale Index Base byte) | ||
| + | * Displacement | ||
| + | * Immediate data | ||
| + | |||
| + | The only field which is present in every instruction is the opcode. Other fields appear depending on the detailed function of the instruction, | ||
| + | |||
| + | =====Registers encoding in the instructions===== | ||
| + | Registers are used in the instructions as the operands which hold the data for calculations, | ||
| + | |||
| + | <table register_16> | ||
| + | < | ||
| + | ^ Bits ^ 000 ^ 001 ^ 010 ^ 011 ^ 100 ^ 101 ^ 110 ^ 111 ^ | ||
| + | | 8-bit registers | ||
| + | | 16-bit registers | ||
| + | | 32-bit registers | ||
| + | </ | ||
| + | |||
| + | In table {{ref> | ||
| + | |||
| + | <table register_64_0> | ||
| + | < | ||
| + | ^ Bits ^ 0.000 ^ 0.001 ^ 0.010 ^ 0.011 ^ 0.100 ^ 0.101 ^ 0.110 ^ 0.111 ^ | ||
| + | | 8-bit registers | ||
| + | | 16-bit registers | ||
| + | | 32-bit registers | ||
| + | | 64-bit registers | ||
| + | </ | ||
| + | |||
| + | In table {{ref> | ||
| + | |||
| + | <table register_64_1> | ||
| + | < | ||
| + | ^ Bits ^ 1.000 ^ 1.001 ^ 1.010 ^ 1.011 ^ 1.100 ^ 1.101 ^ 1.110 ^ 1.111 ^ | ||
| + | | 8-bit registers | ||
| + | | 16-bit registers | ||
| + | | 32-bit registers | ||
| + | | 64-bit registers | ||
| + | </ | ||
| + | |||
| + | In table {{ref> | ||
| + | <table register_ext_0> | ||
| + | < | ||
| + | ^ Bits ^ 0.000 ^ 0.001 ^ 0.010 ^ 0.011 ^ 0.100 ^ 0.101 ^ 0.110 ^ 0.111 ^ | ||
| + | | FPU registers | ||
| + | | MMX registers | ||
| + | | XMM registers | ||
| + | | YMM registers | ||
| + | </ | ||
| + | |||
| + | In table {{ref> | ||
| + | <table register_ext_1> | ||
| + | < | ||
| + | ^ Bits ^ 1.000 ^ 1.001 ^ 1.010 ^ 1.011 ^ 1.100 ^ 1.101 ^ 1.110 ^ 1.111 ^ | ||
| + | | MMX registers | ||
| + | | XMM registers | ||
| + | | YMM registers | ||
| + | </ | ||
| + | =====Instruction prefixes===== | ||
| + | There are four groups of prefixes: | ||
| + | * Lock and repeat | ||
| + | * Segment override and branch hints | ||
| + | * Operand size override | ||
| + | * Address size override | ||
| + | In modern 64-bit processors, REX and VEX prefixes were introduced to extend the number of registers and to enable the RISC-like three-argument instructions. | ||
| + | |||
| + | The **lock prefix** is valid for instructions that work in a read-modify-write manner. An example of such an instruction can be adding a constant or register content to the variable in the memory. | ||
| + | <code asm> lock add QWORD PTR [rax], 5 </ | ||
| + | The lock prefix appears as a single byte with the value 0x0F before the opcode. It disables DMA requests (or any other requests that gain control of the buses) during the execution of the instruction to prevent accidental modification of the memory contents at the same address by both the processor and DMA controller. | ||
| + | |||
| + | The **repeat prefixes** are valid for string instructions. They modify the behaviour of instruction from single to repeated execution. The REP prefix is encoded as a single byte with the value 0xF3. It causes the instruction to repeat up to CX times, decreasing CX with every repetition and appropriately modifying the index registers. The REPE/REPZ (0xF3) and REPNE/REPNZ (0xF2) prefixes are used with string instructions which compare or scan strings. They cause the instruction to repeat with the possibility of finishing the execution if the zero flag is set (REPE) or cleared (REPNE). | ||
| + | |||
| + | The **segment override** prefix is used in the segmented mode of operation. It causes the instruction to access data placed in a segment other than the default. For example, the mov instruction accesses data in the segment pointed by DS, which can be changed to ES with a prefix. | ||
| + | <code asm> | ||
| + | mov BYTE PTR [ebx], 5 ;DS as the default segment | ||
| + | mov BYTE PTR ES:[ebx], 5 ;ES segment override | ||
| + | ;(results in appearance of the byte 0x26 as the prefix) | ||
| + | </ | ||
| + | * 0x2E – CS segment override | ||
| + | * 0x36 – SS segment override | ||
| + | * 0x3E – DS segment override | ||
| + | * 0x26 – ES segment override | ||
| + | * 0x64 – FS segment override | ||
| + | * 0x65 – GS segment override | ||
| + | In 64-bit mode, the CS, SS, DS and ES segment overrides are ignored. | ||
| + | |||
| + | The **branch hint** prefixes can appear together with conditional jump instructions. These prefixes can be used to support the branch prediction unit of the processor in determining if the branch shall be taken (0x3E) or not taken (0x2E). This function is enabled if there is no history in the branch prediction unit yet, and static branch prediction is used. | ||
| + | |||
| + | The **operand size** and **address size override** prefixes can change the default size of operands and addresses. For example, if the processor operates in 32-bit mode, using the 0x66 prefix changes the size of an operand to 16 bits, and using the 0x67 prefix changes the address encoding from 32 bits to 16 bits. To better understand the behaviour of prefixes, let us consider a simple instruction with different variants. Let's start with a 32-bit processor. | ||
| + | <code asm> | ||
| + | ;encoding | ||
| + | mov BYTE PTR [ebx], 0x5 ; | ||
| + | mov WORD PTR [ebx], 0x5 ; | ||
| + | mov DWORD PTR [ebx], 0x5 ;0xC7, 0x03, 0x05, 0x00, 0x00, 0x00 | ||
| + | </ | ||
| + | |||
| + | We can notice that because the default operand size is a 32-bit doubleword, the prefix 0x66 appears in the 16-bit version (WORD PTR). It is also visible that the 8-bit version (BYTE PTR) has a different opcode (0xC6, 0x03 instead of 0xC7, 0x03). Also, the size of the argument is different. | ||
| + | |||
| + | The address override prefix (0x67) appears if we change the register to a 16-bit bx. | ||
| + | <code asm> | ||
| + | ; | ||
| + | mov BYTE PTR [bx], 0x5 ; | ||
| + | mov WORD PTR [bx], 0x5 ; | ||
| + | mov DWORD PTR [bx], 0x5 ;0x67, 0xC7, 0x07, 0x05, 0x00, 0x00, 0x00 | ||
| + | </ | ||
| + | |||
| + | The same situation can be observed if we use a 32-bit address register (ebx) and assemble the same instructions for a 64-bit processor. | ||
| + | <code asm> | ||
| + | ;encoding | ||
| + | mov BYTE PTR [ebx], 0x5 ; | ||
| + | mov WORD PTR [ebx], 0x5 ; | ||
| + | mov DWORD PTR [ebx], 0x5 ;0x67, 0xC7, 0x03, 0x05, 0x00, 0x00, 0x00 | ||
| + | </ | ||
| + | |||
| + | While we use a native 64-bit address register in a 64-bit processor, the address size override prefix disappears. | ||
| + | <code asm> | ||
| + | ;encoding | ||
| + | mov BYTE PTR [rbx], 0x5 ; | ||
| + | mov WORD PTR [rbx], 0x5 ; | ||
| + | mov DWORD PTR [rbx], 0x5 ;0xC7, 0x03, 0x05, 0x00, 0x00, 0x00 | ||
| + | </ | ||
| + | |||
| + | **REX** prefix (0x41, 0x48, 0x49, etc.) appears in long mode, e.g. if we try to enlarge the argument size to 64 bits or use any of the new registers. Long mode enables the possibility of using new registers and 64-bit calculations with the original set of instructions. The REX prefix contains four fixed bits and four control bits as shown in figure {{ref> | ||
| + | |||
| + | <figure rex_fields> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | * bit W: operand size override, it is " | ||
| + | * bit R: extension to ModRM.reg | ||
| + | * bit X: extension to SIB.index | ||
| + | * bir B: extension to ModRM.rm or the SIB.base | ||
| + | Bits R, X and B enable the use of new registers. | ||
| + | |||
| + | <code asm> | ||
| + | ;encoding | ||
| + | mov BYTE PTR [r8], 0x5 ;0x41, 0xC6, 0x00, 0x05 | ||
| + | mov BYTE PTR [r9], 0x5 ;0x41, 0xC6, 0x01, 0x05 | ||
| + | mov BYTE PTR [r10], 0x5 ; | ||
| + | mov DWORD PTR [r8], 0x5 ; | ||
| + | mov QWORD PTR [rbx], 0x5 ;0x48, 0xC7, 0x03, 0x05, 0x00, 0x00, 0x00 | ||
| + | mov QWORD PTR [r8], 0x5 ; | ||
| + | </ | ||
| + | The REX prefix | ||
| + | From the code shown in the last example, you can observe where the register used in the instructions is encoded. | ||
| + | < | ||
| + | =====Instruction opcode===== | ||
| + | The instruction opcode is the mandatory field in every instruction. It encodes the main function of the operation. Expanding the processor' | ||
| + | <code asm> | ||
| + | opcode | ||
| + | 0x0F opcode | ||
| + | 0x0F 0x38 opcode | ||
| + | 0x0F 0x3A opcode | ||
| + | </ | ||
| + | |||
| + | In the new instructions, | ||
| + | * 0xC4 Three-byte VEX | ||
| + | * 0xC5 Two-byte VEX | ||
| + | * 0x8F Three-byte XOP | ||
| + | VEX-encoded instructions are written with V at the beginning. Let's look at the example of the blending instruction. | ||
| + | <code asm> | ||
| + | ;encoding | ||
| + | blendvpd xmm0, xmm1 ; | ||
| + | vblendvpd xmm0, xmm1, xmm2, xmm3 ;0xC4, 0xE3, 0x71, 0x4B, 0xC2, 0x30 | ||
| + | </ | ||
| + | The first blendvpd instruction has only two arguments; in this encoding scheme is not possible to encode more. It uses the mandatory prefix 0x66 and 0x0F, 0x38 escape sequence. The second version, vblendvpd, has four arguments. It is encoded with a three-byte VEX escape sequence 0xC4, 0xE3, 0x71. | ||
| + | |||
| + | =====MOD R/M byte===== | ||
| + | The ModR/M byte encodes the addressing mode, a register which is used as an operand in the instruction, | ||
| + | |||
| + | <figure modrm_byte> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | In general, the fields have meaning: | ||
| + | * Mod - Mode. This 2-bit field gives the register/ | ||
| + | * Reg - Register. This 3-bit field specifies one of the general-purpose registers used as the operand. It can also be the opcode extension. | ||
| + | * R/M - Register/ | ||
| + | |||
| + | In x86, the Mod field specifies one of four possible memory addressing modes, and the R/M field specifies which register, or pair of registers, is used for address calculation. If the Mod field is 11 (binary), the R/M field specifies the second register in the instruction. Details are shown in table {{ref> | ||
| + | |||
| + | <table modrm_16> | ||
| + | < | ||
| + | ^ ^ R/M |||||||| | ||
| + | ^ Mod ^ 000 ^ 001 ^ 010 ^ 011 ^ 100 ^ 101 ^ 110 ^ 111 ^ | ||
| + | ^ 00 | [BX + SI] | [BX + DI] | [BP + SI] | [BP + DI] | [SI] | [DI] | [disp16] | ||
| + | ^ 01 | [BX + SI + disp8] | ||
| + | ^ 10 | [BX + SI + disp16] | ||
| + | ^ 11 | AX/AL | CX/CL | DX/DL | BX/BL | SP/AH | BP/CH | SI/DH | DI/BH | | ||
| + | |||
| + | </ | ||
| + | |||
| + | Let's look at some examples of instruction encoding. First, look at the data transfer between two registers. | ||
| + | <code asm> | ||
| + | ; | ||
| + | mov al, dl ;0x88, 0xD0 11 010 000 | ||
| + | mov ax, dx ;0x89, 0xD0 11 010 000 | ||
| + | mov dx, si ;0x89, 0xF2 11 110 010 | ||
| + | mov si, dx ;0x89, 0xD6 11 010 110 | ||
| + | </ | ||
| + | Notice that in the first and second lines, different opcodes are used, but the MOD R/M bytes are identical. The type of instruction determines the order of data transfer. | ||
| + | |||
| + | Now, a few examples of indirect addressing without displacement. | ||
| + | <code asm> | ||
| + | ; | ||
| + | mov dx, | ||
| + | mov dx, | ||
| + | mov dx,[bx+di] ;0x8B, 0x11 00 010 001 Reg. only addr. | ||
| + | mov cx,[bx+di] ;0x8B, 0x09 00 001 001 Reg. only addr. | ||
| + | </ | ||
| + | |||
| + | Now, a few examples of indirect addressing with displacement. | ||
| + | <code asm> | ||
| + | ; | ||
| + | mov dx,[bp+62] ;0x8B, 0x56, 0x3E 01 010 110 | ||
| + | mov [bp+62],dx ;0x89, 0x56, 0x3E 01 010 110 | ||
| + | mov dx,[si+13] ;0x8B, 0x54, 0x0D 01 010 100 | ||
| + | mov si, | ||
| + | </ | ||
| + | If we look at the first two lines, we can observe that the MOD R/M bytes are identical. The only difference is the opcode, which determines the direction of the data transfer. | ||
| + | |||
| + | Notice also that the last instruction is encoded as BP + displacement, | ||
| + | |||
| + | In 32-bit mode, registers used for addressing can be specified with the SIB byte, but this is not always the case. If a single register is used (with some exceptions), | ||
| + | In 64-bit long mode, the MOD R/M byte encoding works in a similar manner to 32-bit, with the difference that the MOD R/M byte is extended with R and B bits from the REX prefix, enabling the use of more registers in instructions. | ||
| + | |||
| + | <table modrm_640> | ||
| + | < | ||
| + | ^ ^ 0.R/M |||||||| | ||
| + | ^ Mod ^ 0.000 ^ 0.001 ^ 0.010 ^ 0.011 ^ 0.100 ^ 0.101 ^ 0.110 ^ 0.111 ^ | ||
| + | ^ 00 | [RAX] | [RCX] | [RDX] | [RBX] | [SIB] | [RIP + disp] | [SI] | [DI] | | ||
| + | ^ 01 | [RAX + disp8] | ||
| + | ^ 10 | [RAX + disp32] | ||
| + | ^ 11 | RAX | RCX | RDX | RBX | RSP | RBP | RSI | RDI | | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | <table modrm_641> | ||
| + | < | ||
| + | ^ ^ 1.R/M |||||||| | ||
| + | ^ Mod ^ 1.000 ^ 1.001 ^ 1.010 ^ 1.011 ^ 1.100 ^ 1.101 ^ 1.110 ^ 1.111 ^ | ||
| + | ^ 00 | [R8] | [R9] | [R10] | [R11] | [SIB] | [RIP + disp] | [R14] | [R15] | | ||
| + | ^ 01 | [R8 + disp8] | ||
| + | ^ 10 | [R8 + disp32] | ||
| + | ^ 11 | R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 | | ||
| + | |||
| + | </ | ||
| + | |||
| + | =====Scale Index Base byte===== | ||
| + | The SIB byte was added in 32-bit machines to enable the use of any register in addressing. It encodes the scaling factor, index and base register used in address calculations. As a result, the choice of registers for calculations is significantly wider. For details, please refer to the addressing modes section. The SIB byte has the fields as shown in the figure {{ref> | ||
| + | |||
| + | <figure sib_byte> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | The scaling factor specifies the number 1, 2, 4 or 8 by which the content of the index register is multiplied in the process of the address calculation as presented in table {{ref> | ||
| + | |||
| + | <table SIB_scale> | ||
| + | < | ||
| + | ^ Bits ^ 00 ^ 01 ^ 10 ^ 11 ^ | ||
| + | | Scaling factor | ||
| + | </ | ||
| + | |||
| + | The index and base fields specify the index and base registers, respectively. In 32-bit processors, the register encoding is similar to that presented in table {{ref> | ||
| + | |||
| + | <table SIB_index> | ||
| + | < | ||
| + | ^ Bits index ^ 000 ^ 001 ^ 010 ^ 011 ^ 100 ^ 101 ^ 110 ^ 111 ^ | ||
| + | | 32-bit index register | ||
| + | </ | ||
| + | |||
| + | <table SIB_base> | ||
| + | < | ||
| + | ^ Bits base ^ 000 ^ 001 ^ 010 ^ 011 ^ 100 ^ 101 ^ 110 ^ 111 ^ | ||
| + | | 32-bit base register | ||
| + | </ | ||
| + | |||
| + | In 64-bit long mode, the SIB byte is extended with X and B bits from the REX prefix, enabling the use of more registers in instructions. Details are presented in tables {{ref> | ||
| + | |||
| + | <table SIB_index64> | ||
| + | < | ||
| + | ^ Bits X.Index | ||
| + | | 64-bit index register | ||
| + | || | ||
| + | ^ Bits X.Index | ||
| + | | 32-bit index register | ||
| + | |||
| + | </ | ||
| + | |||
| + | <table SIB_base64> | ||
| + | < | ||
| + | ^ Bits B.Base | ||
| + | | 32-bit base register | ||
| + | || | ||
| + | ^ Bits B.Base | ||
| + | | 32-bit base register | ||
| + | </ | ||
| + | |||
| + | In the tables {{ref> | ||
| + | |||
| + | Let's look at some code examples, considering the 32-bit version first. In all instructions, | ||
| + | <code asm> | ||
| + | ;MOD R/M (second byte) is 0x04 for all instructions: | ||
| + | ; | ||
| + | ; | ||
| + | |||
| + | ;SIB (third byte) is 0x0B, 0x4B, 0x8B or 0xCB: | ||
| + | ; | ||
| + | mov eax, [ebx+ecx] | ||
| + | mov eax, [ebx+ecx*2] ;0x8B, 0x04, 0x4B 01 | ||
| + | mov eax, [ebx+ecx*4] ;0x8B, 0x04, 0x8B 10 | ||
| + | mov eax, [ebx+ecx*8] ;0x8B, 0x04, 0xCB 11 | ||
| + | </ | ||
| + | |||
| + | And other examples for x64 processors. The SIB byte is extended with bits from the REX prefix. We'll start with the similar examples as shown for 32-bit machines. | ||
| + | |||
| + | <code asm> | ||
| + | ;REX prefix (first byte) is 0x48 for all instructions: | ||
| + | ; | ||
| + | ; | ||
| + | ; | ||
| + | ; | ||
| + | ; | ||
| + | |||
| + | ;MOD R/M (second byte) is 0x04 for all instructions: | ||
| + | ; | ||
| + | ; | ||
| + | |||
| + | ; | ||
| + | mov rax, [rbx+rcx] | ||
| + | mov rax, [rbx+rcx*2] ;0x48, 0x8B, 0x04, 0x4B 01 | ||
| + | mov rax, [rbx+rcx*4] ;0x48, 0x8B, 0x04, 0x8B 10 | ||
| + | mov rax, [rbx+rcx*8] ;0x48, 0x8B, 0x04, 0xCB 11 | ||
| + | </ | ||
| + | |||
| + | If any of the new registers (R8-R15) is used in the instruction, | ||
| + | |||
| + | <code asm> | ||
| + | ; | ||
| + | mov rax, [r10+rcx] | ||
| + | mov rax, [rbx+r11] | ||
| + | mov r12, [rbx+rcx] | ||
| + | |||
| + | ;Last instruction has the MOD R/M REG field extended | ||
| + | ;by the R bit from the REX prefix. | ||
| + | ; | ||
| + | ; | ||
| + | </ | ||
| + | |||
| + | Certainly, the presented examples do not exhaust all possible situations. For a more detailed explanation, | ||
| + | =====Displacement===== | ||
| + | Displacement gives the offset for memory operands. Depending on the addressing mode, it can be the direct memory address or an additional offset added to the contents of the base, index register or both. Displacement can be 1, 2, or 4 bytes long. Some instructions allow using an 8-byte displacement. In these instructions, | ||
| + | |||
| + | =====Immediate===== | ||
| + | Some instructions require an immediate value. The instruction determines the length of the immediate value. The immediate can be 1, 2, 4 or 8 bytes long. When an 8-byte immediate value is encoded, no displacement can be encoded. | ||
| + | |||
| + | < | ||
| + | MazeGen, x86 and amd64 instruction reference ((https:// | ||