Detailed Explanation of Operand Size Prefix in Assembly Language

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:

  1. Flexibility: Allows the same instruction to handle operands of different sizes
  2. Compatibility: Ensures old code runs correctly on new processors
  3. 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.

Detailed Explanation of Operand Size Prefix in Assembly Language

Leave a Comment