Function and Significance of Operand Size Prefix
Historical Background and Design Principles
The x86 processor family has evolved from 16-bit to 32-bit and then to 64-bit. To maintain backward compatibility, Intel introduced the operand size prefix mechanism:
- During the 8086/8086 Era: 256 opcodes were almost entirely used for 8-bit and 16-bit operands
- During the 80386 Era: 32-bit operands needed to be supported, but the opcode space was limited
- Solution: Use prefix bytes to switch operand sizes
Working Mechanism of Operand Size Prefix
66h - Operand Size Prefix
Default Behavior Rules:
- 16-bit Mode: Default 16-bit operands, 66h prefix enables 32-bit operands
- 32-bit Mode: Default 32-bit operands, 66h prefix enables 16-bit operands
- 8-bit Operands: No prefix is ever needed
Code Example Analysis
Operand Size in 16-bit Mode
; Compile target: 16-bit processor (.286)
.model small
.286
.stack 100h
.code
main PROC
; 16-bit register operations - no prefix
mov ax, dx ; Machine code: 8B C2
mov bx, cx ; Machine code: 8B D9
; 8-bit register operations - no prefix
mov al, dl ; Machine code: 8A C2
mov bl, cl ; Machine code: 8A D9
; 32-bit operation requires prefix (if supported)
; db 66h ; Operand size prefix
; mov eax, edx ; May not be supported in pure 16-bit mode
main ENDP
end main
Operand Size in 32-bit Mode
; Compile target: 32-bit processor (.386)
.model small
.386
.stack 100h
.code
main PROC
; 32-bit register operations - no prefix (default in 32-bit mode)
mov eax, edx ; Machine code: 8B C2
mov ebx, ecx ; Machine code: 8B D9
; 16-bit register operations - require 66h prefix
mov ax, dx ; Machine code: 66 8B C2
mov bx, cx ; Machine code: 66 8B D9
; 8-bit register operations - no prefix
mov al, dl ; Machine code: 8A C2
mov bl, cl ; Machine code: 8A D9
main ENDP
end main
Summary of Register Mode Instructions
ModR/M Byte Structure
Register mode instructions use the ModR/M byte to encode register operands:
ModR/M byte: [mod:2][reg:3][r/m:3]
The mod field of 11 indicates register mode
Register Encoding Examples
; 16-bit register operation examples
mov ax, bx ; 89 D8: mod=11, reg=011(BX), r/m=000(AX)
mov cx, dx ; 89 D1: mod=11, reg=010(DX), r/m=001(CX)
mov dx, ax ; 89 C2: mod=11, reg=000(AX), r/m=010(DX)
; 8-bit register operation examples
mov al, bl ; 88 D8: mod=11, reg=011(BL), r/m=000(AL)
mov cl, dl ; 88 D1: mod=11, reg=010(DL), r/m=001(CL)
Summary of Memory Mode Instructions
Memory Addressing Mode
When the mod field is not 11, it indicates memory operands:
; Direct memory addressing (mod=00)
mov ax, [1234h] ; Direct address
mov eax, [buffer] ; Variable address
; Register indirect addressing (mod=00, r/m specifies base register)
mov ax, [bx] ; 16-bit: using BX, SI, DI, BP
mov eax, [ebx] ; 32-bit: using EAX, EBX, ECX, EDX, etc.
; Addressing with displacement (mod=01 or 10)
mov ax, [bx+10] ; 8-bit displacement
mov eax, [ebx+100h] ; 32-bit displacement
; Base-indexed addressing
mov ax, [bx+si] ; 16-bit base+index
mov eax, [ebx+esi*4] ; 32-bit addressing with scale
Memory Operation Examples
.data
buffer dw 1234h, 5678h
darray dd 10 dup(0)
.code
; Direct memory access
mov ax, buffer ; AX = 1234h
mov eax, darray[4] ; EAX = darray[1]
; Register indirect
mov bx, offset buffer
mov ax, [bx] ; AX = 1234h
; Indirect with displacement
mov ax, [bx+2] ; AX = 5678h
; 32-bit addressing
mov esi, offset darray
mov eax, [esi+8] ; EAX = darray[2]
Comprehensive Application Example
Mixed Operand Size Programming
.386
.model flat, stdcall
.stack 4096
.data
wVal dw 1000h
dVal dd 12345678h
bVal db 55h
.code
main PROC
; 32-bit default operation
mov eax, dVal ; EAX = 12345678h (no prefix)
; 16-bit operation requires prefix
mov ax, wVal ; AX = 1000h (66 8B 05 xx xx xx xx)
; 8-bit operation no prefix
mov al, bVal ; AL = 55h (A0 xx xx xx xx)
; Mixed size operations
movzx eax, wVal ; Zero-extend 16-bit to 32-bit
movsx ecx, bVal ; Sign-extend 8-bit to 32-bit
; Size prefix in memory operations
mov word ptr [eax], 1000h ; 16-bit memory write
mov dword ptr [ebx], 12345678h ; 32-bit memory write
; 32-bit addressing with scale
mov esi, 4
mov eax, darray[esi*4] ; Access darray[4]
ret
main ENDP
end main
Real Application Scenario
; String copy function - demonstrating different operand sizes
str_copy PROC
; 32-bit mode, using 16-bit operations requires prefix
push ebp
mov ebp, esp
mov esi, [ebp+8] ; Source string pointer (32-bit)
mov edi, [ebp+12] ; Destination string pointer (32-bit)
copy_loop:
mov al, [esi] ; Read one byte (8-bit operation)
test al, al ; Check for null terminator
jz copy_done
mov [edi], al ; Write one byte
inc esi
inc edi
jmp copy_loop
copy_done:
mov byte ptr [edi], 0 ; Write null terminator
pop ebp
ret
str_copy ENDP
Conclusion
The operand size prefix is a key mechanism for maintaining backward compatibility in the x86 architecture:
- Flexibility: Allows the same instruction to handle operands of different sizes
- Compatibility: Ensures old code runs correctly on new processors
- Efficiency: Expands the instruction set through prefixing without increasing opcode complexity
Understanding operand size prefixes is crucial for writing efficient and portable assembly code, especially in mixed 16-bit/32-bit/64-bit programming environments.
