Last modified: Sun Jun 21 20:07:00 UTC+0200 2026 © A. Tarpai
386 Addressing Modes and Effective Address calculation
The 386 is both a 32- and a 16-bit CPU at the same time: 32- and a 16-bit addressing modes and data movements can be mixed between- and even within one instruction using 66h/67h prefix bytes.
- D-bit determines the default addressing style and default data movement size
- 67h address-prefix: switches between 8086 Legacy MODR/M or 386 MODR/M+SIB style addressing modes (along with 16- or 32-bit EA calculation)
- 66h operand-prefix: switches between 16- or 32-bit data movement size, when W=1 in opcode or as implied
Addressing Modes
When 16-bit addressing mode is used, the MODR/M byte is interpreted as a 16-bit addressing mode specifier.
When 32-bit addressing mode is used, the MODR/M byte is interpreted as a 32-bit addressing mode specifier.
SIB = SCALE-INDEX-BASE
16-BIT MEMORY ADDRESSING MODES 32-BIT MEMORY ADDRESSING MODES
______________________________ ___________________________________
D=0 or D=1 or
D=1 and 67h address-prefix D=0 and 67h address-prefix
16-bit 32-bit
EFFECTIVE ADDRESS CALCULATION: EFFECTIVE ADDRESS CALCULATION:
[BP/BX + SI/DI + D8/D16] [BASE + INDEX*SCALE + D8/D32]
X X X X BP/BX BASE REG X X X X X X X X BASE REG
X X X X SI/DI INDEX REG X X X X X X X X SCALED INDEX REG
X X X X DISPLACEMENT X X X X X X X X DISPLACEMENT
--------------------------- ------------------------------------
X X X X 16-BIT OFFSET X X X X X X X X 32-BIT OFFSET
Legacy 8086-MODR/M byte: 386-MODR/M + optional SIB byte:
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-----+--------+-------+ +-----+--------+-------+ +-----+--------+-------+
| MOD | | R/M | | MOD | | BASE | |SCALE| INDEX | BASE |
+-----+--------+-------+ +-----+--------+-------+ +-----+--------+-------+
MODR/M BYTE MODR/M BYTE SIB BYTE
EAX ECX EDX EBX - EBP ESI EDI
R/M OPERAND ADDRESS R/M OPERAND ADDRESS INDEX: 000 001 010 011 100 101 110 111
000 BX + SI + DISP 000 EAX + DISP EAX 000
001 BX + DI + DISP 001 ECX + DISP ECX 001
010 BP + SI + DISP 010 EDX + DISP EDX 010
011 BP + DI + DISP 011 EBX + DISP EBX 011
100 SI + DISP 100 sib + DISP ---> ESP 100 SIB-BASE + INDEX << SCALE
101 DI + DISP 101 EBP + DISP* EBP 101*
110 BP + DISP* 110 ESI + DISP ESI 110
111 BX + DISP 111 EDI + DISP EDI 111
MOD DISPLACEMENT MOD DISPLACEMENT SCALE:
00 NO DISPLACEMENT 00 NO DISPLACEMENT 00 NO SCALING
01 1 byte DISP (sign-extend) 01 1 byte DISP (sign-extend) 01 2
10 2 byte DISP 10 4 byte DISP 10 4
11 REGISTER ADDRESSING 11 REGISTER ADDRESSING 11 8
* Direct Memory Addressing * Direct Memory Addressing - 100 means NO INDEX
MOD=00: 2 byte DISP ONLY MOD=00: 4 byte DISP ONLY
8/16-BIT REGISTER ADDRESSING 8/32-BIT REGISTER ADDRESSING
______________________________ ___________________________________
D=0 or D=1 or
D=1 and 66h operand-prefix D=0 and 66h operand-prefix
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-----+--------+-------+ +-----+--------+-------+
| 1 1 | | REG | | 1 1 | | REG |
+-----+--------+-------+ +-----+--------+-------+
MODR/M BYTE MODR/M BYTE
REG W=0 W=1 REG W=0 W=1
000 AL AX 000 AL EAX
001 CL CX 001 CL ECX
010 DL DX 010 DL EDX
011 BL BX 011 BL EBX
100 AH SP 100 AH ESP
101 CH BP 101 CH EBP
110 DH SI 110 DH ESI
111 BH DI 111 BH EDI
(67h has no effect)
EFFECTIVE ADDRESS CALCULATION with implied DS or SS
- 8086 MODR/M: when BP is used as the base register, the default segment register is SS. In all other cases the default segment is DS.
- 386 MODR/M: when EBP (in MODR/M or SIB byte) or ESP (in SIB byte) used as the base register, the default segment register is SS. In all other cases the default segment is DS.
16-BIT MEMORY ADDRESSING MODES 32-BIT MEMORY ADDRESSING MODES
______________________________ _________________________________________________________________________
D=0 or D=1 or
D=1 and 67h address-prefix D=0 and 67h address-prefix
WITH SCALED INDEX NO INDEX
MOD R/M Effective MOD R/M Effective SIB Effective Effective
Address BASE Address BASE Address Address†
00 000 [BX + SI] 00 000 [EAX] 000 [EAX + IDX] [EAX]
00 001 [BX + DI] 00 001 [ECX] 001 [ECX + IDX] [ECX]
00 010 SS:[BP + SI] 00 010 [EDX] 010 [EDX + IDX] [EDX]
00 011 SS:[BP + DI] 00 011 [EBX] 011 [EBX + IDX] [EBX]
00 100 [SI] 00 100 SIB ---------> 100 SS:[ESP + IDX] SS:[ESP]
00 101 [DI] 00 101 [D32]* 101 [D32 + IDX]** [D32]*
00 110 [D16]* 00 110 [ESI] 110 [ESI + IDX] [ESI]
00 111 [BX] 00 111 [EDI] 111 [EDI + IDX] [EDI]
01 000 [BX + SI + D8] 01 000 [EAX + D8] 000 [EAX + IDX + D8] [EAX + D8]
01 001 [BX + DI + D8] 01 001 [ECX + D8] 001 [ECX + IDX + D8] [ECX + D8]
01 010 SS:[BP + SI + D8] 01 010 [EDX + D8] 010 [EDX + IDX + D8] [EDX + D8]
01 011 SS:[BP + DI + D8] 01 011 [EBX + D8] 011 [EBX + IDX + D8] [EBX + D8]
01 100 [SI + D8] 01 100 SIB ---------> 100 SS:[ESP + IDX + D8] SS:[ESP + D8]
01 101 [DI + D8] 01 101 SS:[EBP + D8] 101 SS:[EBP + IDX + D8] SS:[EBP + D8]
01 110 SS:[BP + D8] 01 110 [ESI + D8] 110 [ESI + IDX + D8] [ESI + D8]
01 111 [BX + D8] 01 111 [EDI + D8] 111 [EDI + IDX + D8] [EDI + D8]
10 000 [BX + SI + D16] 10 000 [EAX + D32] 000 [EAX + IDX + D32] [EAX + D32]
10 001 [BX + DI + D16] 10 001 [ECX + D32] 001 [ECX + IDX + D32] [ECX + D32]
10 010 SS:[BP + SI + D16] 10 010 [EDX + D32] 010 [EDX + IDX + D32] [EDX + D32]
10 011 SS:[BP + DI + D16] 10 011 [EBX + D32] 011 [EBX + IDX + D32] [EBX + D32]
10 100 [SI + D16] 10 100 SIB ---------> 100 SS:[ESP + IDX + D32] SS:[ESP + D32]
10 101 [DI + D16] 10 101 SS:[EBP + D32] 101 SS:[EBP + IDX + D32] SS:[EBP + D32]
10 110 SS:[BP + D16] 10 110 [ESI + D32] 110 [ESI + IDX + D32] [ESI + D32]
10 111 [BX + D16] 10 111 [EDI + D32] 111 [EDI + IDX + D32] [EDI + D32]
ESP cannot be R/M BASE ESP cannot be INDEX
(100 means SIB) (100 means NO INDEX)
idx = scaled index register
SS marked only. Otherwise DS implied
* BP- or EBP-relative addressing without DISPLACEMENT (MOD=00) always means Direct Memory: full DISPLACEMENT follows with implied DS, and that is the Effective Address.
** Also Direct Memory with implied DS + scaled index. A funny one. It does not mean SS:[EBP+idx]. This can be encoded by [EBP+idx+0] using zero-byte displacement – similar to [EBP+0] for [EBP].
† These modes are redundant and used practically only for [ESP], [ESP + d8] and [ESP + d32] missing from MODR/M encoding.
386 MODR/M SIB:
- ESP cannot be base in MODR/M byte (it means SIB follows) - but can be base in SIB
- SIB 100 NO INDEX: ESP cannot be scaled index. 100 means NO INDEX REG and SCALE must be 00 (otherwise the effective address is undefined, 386 manual).
Test: Redundant addressing mode
Compare [ebp + 8] with MODR/M and MODR/M + SIB NO INDEX. Manually _emit code bytes (VC++):
8B 45 08 mov eax, [ebp + 8] 8B 44 25 08 mov eax, [ebp + NOIDX + 8] <-- not valid in assembler _emit 8Bh // mov _emit (1<<6) | (0<<3) | (4) // MOD=01 REG=EAX R/M=SIB _emit (0<<6) | (4<<3) | (5) // SCALE=0 INDEX=NONE BASE=EBP _emit 8h
Run code. Same as MODR/M without SIB (tested, same result).
66/67H Prefixes
This addressing style - and default operand size - can be switched between 16/32-bit modes on a by instruction-basis, even mixed within one instruction using 66/67H prefix bytes.
67H Address-Size Prefix
Address Size means legacy- or 32-bit ADDRESSING MODES, using 16- or 32-bit format MODR/M byte and to calculate a 16- or 32-bit Effective Address (EA offset):
32-bit optional optional BYTE- or
MODR/M SIB DWORD displacement
+-----+ +-----+ +-----+-----+-----+-----+
D 32-bit addressing mode: | M/R | | SIB | | | | --> 32-bit EA offset
\ / +-----+ +-----+ +-----+-----+-----+-----+
\ /
/ \
/ \ +-----+ +-----+-----+
67H 16-bit addressing mode: | M/R | | | | --> 16-bit EA offset
+-----+ +-----+-----+
16-bit optional BYTE- or
legacy WORD displacement
MODR/M
The displacement size is 0, 1 or (2 or 4 bytes) based on MOD=00, 01 or 10.
66H Operand-Size Prefix
D=0: opcode W-bit encodes BYTE or WORD operation (16-bit legacy mode)
D=1: same opcode encodes BYTE or DWORD operation (32-bit mode)
+-----+-----+-----+-----+
D 8/32-bit operand size | | | BYTE or DWORD DATA
\ / +-----+-----+-----+-----+
\ /
/ \
/ \ +-----+-----+
66H 8/16-bit operand size | | | BYTE or WORD DATA
+-----+-----+
This is also true for every implicite data storage and manipulation:
- register reference (AX or EAX)
- immediate values following opcode (2 or 4 bytes)
- immediate storage for indirect references (eg. LDS, JMP)
- PUSH/POP (2 or 4 bytes)
Examples
Immediate example:
CPU interpretation of the same opcode (B8 move immediate to Accu) based on current CS D=0 or D=1. NASM assembler:
opcodes D=1 opcodes D=0
[BITS 32] [BITS 16]
B8 FFFFFFFF mov eax, -1 B8 FFFF MOV AX, -1
Addressing Mode example:
CPU interpretation of the same opcodes with prefixes based on current CS D=0 or D=1. NASM assembler:
opcodes D=1
[BITS 32]
8B 00 mov eax, [eax] ; Native 32-bit: 32-bit addressing mode and moves 32-bit DWORD
67 8B 00 mov eax, [bx+si] ; Moves 32-bit DWORD but uses 16-bit addressing mode
66 8B 00 mov ax, [eax] ; 32-bit addressing mode but moves 16-bit WORD
66 67 8B 00 mov ax, [bx+si] ; Legacy 16-bit addressing mode and moves 16-bit WORD
opcodes D=0
[BITS 16]
8B 00 MOV AX, [BX+SI] ; Native 16-bit: 16-bit addressing mode and moves 16-bit WORD
67 8B 00 MOV AX, [EAX] ; Moves 16-bit WORD but uses 32-bit addressing mode
66 8B 00 MOV EAX, [BX+SI] ; 16-bit addressing mode but moves 32-bit DWORD
66 67 8B 00 MOV EAX, [EAX] ; Uses 32-bit addressing mode and moves 32-bit DWORD
These two prefixes are absolutely orthogonal:
CS Segment Default D-bit | 0 0 0 0 1 1 1 1 Operand-Size Prefix 66H | N N Y Y N N Y Y Address-Size Prefix 67H | N Y N Y N Y N Y --------------------------+------------------------------------------ Effective Operand Size | 16 16 32 32 32 32 16 16 Effective Address Size | 16 32 16 32 32 16 32 16
(INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986)